diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e51343e3..286469cc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,8 +15,8 @@ jobs: - uses: actions/checkout@v2 - name: Install run: | - wget https://github.com/premake/premake-core/releases/download/v5.0.0-alpha16/premake-5.0.0-alpha16-linux.tar.gz - tar -xvf premake-5.0.0-alpha16-linux.tar.gz + wget -q https://github.com/premake/premake-core/releases/download/v5.0.0-alpha16/premake-5.0.0-alpha16-linux.tar.gz + tar -xf premake-5.0.0-alpha16-linux.tar.gz sudo chmod a+x premake5 sudo mv premake5 /usr/local/bin diff --git a/.rive_head b/.rive_head index 6f9ea6fc..1e827643 100644 --- a/.rive_head +++ b/.rive_head @@ -1 +1 @@ -e2328daeb02a92171cd3c0ab78c3c76695a9dd98 +b2d27b6bde96e68a9cac4f3b7efd12782e9351cd diff --git a/Dockerfile b/Dockerfile index 31ab9376..e3e6d188 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,8 +3,8 @@ FROM dart:stable RUN apt update && apt-get -y install unzip zip clang cmake ninja-build pkg-config libgtk-3-dev xvfb cargo wget g++ WORKDIR / -RUN wget https://github.com/premake/premake-core/releases/download/v5.0.0-alpha15/premake-5.0.0-alpha15-linux.tar.gz -RUN tar -xvf premake-5.0.0-alpha15-linux.tar.gz +RUN wget -q https://github.com/premake/premake-core/releases/download/v5.0.0-beta2/premake-5.0.0-beta2-linux.tar.gz +RUN tar -xf premake-5.0.0-beta2-linux.tar.gz RUN mv premake5 /usr/bin/ ENV LDFLAGS="-pthreads" diff --git a/build.sh b/build.sh index ec40d626..e89795d5 100755 --- a/build.sh +++ b/build.sh @@ -32,8 +32,8 @@ if [ "$OPTION" = 'help' ]; then else build() { echo "Building Rive for platform=$platform option=$OPTION" - echo premake5 gmake2 --with_rive_text --with_rive_audio=system "$1" - PREMAKE="premake5 gmake2 --with_rive_text --with_rive_audio=system $1" + echo premake5 gmake2 --with_rive_text --with_rive_audio=system --with_rive_layout "$1" + PREMAKE="premake5 gmake2 --with_rive_text --with_rive_audio=system --with_rive_layout $1" eval "$PREMAKE" if [ "$OPTION" = "clean" ]; then make clean diff --git a/build/build_rive.bat b/build/build_rive.bat new file mode 100644 index 00000000..a0d7a193 --- /dev/null +++ b/build/build_rive.bat @@ -0,0 +1,10 @@ +@echo off +REM we could check where vs is install via the registery like stated here https://superuser.com/questions/539666/how-to-get-the-visual-studio-installation-path-in-a-batch-file but i think that is overkill +if exist "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" CALL "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" +else ( + if exist CALL "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\VsDevCmd.bat" + else echo "Visual Studio 2022 does not appear to be installed, please install visual studio to C:\Program Files\Microsoft Visual Studio" +) + +cd %CD% +sh build_rive.sh %* diff --git a/build/build_rive.ps1 b/build/build_rive.ps1 new file mode 100644 index 00000000..b223f9c5 --- /dev/null +++ b/build/build_rive.ps1 @@ -0,0 +1,15 @@ +# requires Set-ExecutionPolicy Bypass -Scope CurrentUser +$CWD = Get-Location +if (Test-Path "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\Launch-VsDevShell.ps1" -PathType Leaf) { + & "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\Launch-VsDevShell.ps1" +} else { + if ("C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\Launch-VsDevShell.ps1") { + & 'C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\Launch-VsDevShell.ps1' + } + else { + Write-Error "Visual Studio 2022 does not appear to be installed, please install visual studio to C:\Program Files\Microsoft Visual Studio" + exit + } +} +Set-Location $CWD +sh build_rive.sh $args \ No newline at end of file diff --git a/build/build_rive.sh b/build/build_rive.sh new file mode 100755 index 00000000..e091ec0a --- /dev/null +++ b/build/build_rive.sh @@ -0,0 +1,293 @@ +#!/bin/bash + +# build_rive.sh: build any standard Rive sub-project. +# +# To use this script: +# +# 1) add packages/runtime/build to $PATH +# 2) cd to directory with a premake5.lua +# 3) run build_rive.sh with desired flags (in any order) +# +# Build for the host machine: +# +# build_rive.sh # debug build +# build_rive.sh release # release build +# build_rive.sh release clean # clean, followed by a release build +# build_rive.sh xcode # generate and build an xcode workspace +# build_rive.sh ninja # use ninja (experimental) for a debug build +# build_rive.sh ninja release # use ninja (experimental) for a release build +# build_rive.sh ninja --with_vulkan # extra parameters get forwarded to premake +# +# Generate compile_commands.json for code completion engines: +# +# build_rive.sh compdb # code completion for a host build +# build_rive.sh compdb android --with_vulkan # code completion for android with vulkan enabled +# +# Specify build targets after "--": +# +# build_rive.sh -- gms goldens +# build_rive.sh ninja release -- imagediff +# +# Build for android: +# +# build_rive.sh android # debug build, defaults to arm64 +# build_rive.sh ninja android arm # release arm32 build using ninja +# +# Build for ios: +# +# build_rive.sh ios # debug build, arm64 only +# build_rive.sh ios release # release build, arm64 only +# build_rive.sh iossim # debug build, defaults to "universal" (x64 and arm64) architecture +# +# Build for wasm: +# +# build_rive.sh ninja release wasm # release build +# build_rive.sh ninja release wasm --with-webgpu # release build, also enable webgpu +# +# Incremental builds: +# +# build_rive.sh # initial build +# build_rive.sh # any repeat command does an incremental build +# build_rive.sh --with_vulkan <- FAILS!! # a repeat build with different arguments fails +# build_rive.sh clean --with_vulkan # a clean build with different arguments is OK +# build_rive.sh rebuild out/debug # use "rebuild" on a specific OUT directory to relaunch the +# # build with whatever args it got configured with initially +# build_rive.sh rebuild out/debug gms goldens # args after OUT get forwarded to the buildsystem + +set -e +set -o pipefail + +# Detect where build_rive.sh is located on disk. +# https://stackoverflow.com/questions/59895/how-do-i-get-the-directory-where-a-bash-script-is-located-from-within-the-script +SOURCE=${BASH_SOURCE[0]} +while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink + DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd ) + SOURCE=$(readlink "$SOURCE") + # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located + [[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE +done +SCRIPT_DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd ) + +case "$(uname -s)" in + Darwin*) + if [[ $(arch) = "arm64" ]]; then + HOST_MACHINE="mac_arm64" + else + HOST_MACHINE="mac_x64" + fi + NUM_CORES=$(($(sysctl -n hw.physicalcpu) + 1)) + ;; + MINGW*|MSYS*) + HOST_MACHINE="windows" + NUM_CORES=$NUMBER_OF_PROCESSORS + ;; + Linux*) + HOST_MACHINE="linux" + NUM_CORES=$(grep -c processor /proc/cpuinfo) + ;; +esac + +if [[ "$1" = "rebuild" ]]; then + # Load args from an existing build. + RIVE_OUT=$2 + shift + shift + + if [ ! -d $RIVE_OUT ]; then + echo "OUT directory '$RIVE_OUT' not found." + exit -1 + fi + + ARGS_FILE=$RIVE_OUT/.rive_premake_args + if [ ! -f $ARGS_FILE ]; then + echo "Premake args file '$ARGS_FILE' not found." + exit -1 + fi + + RIVE_PREMAKE_ARGS="$(< $ARGS_FILE)" + RIVE_BUILD_SYSTEM="$(awk '{print $1}' $ARGS_FILE)" # RIVE_BUILD_SYSTEM is the first word in the args. + if grep -e "--arch=" $ARGS_FILE > /dev/null; then + RIVE_ARCH="$(sed -r 's/^.*--arch=([^ ]*).*$/\1/' $ARGS_FILE)" + fi +else + # New build. Parse arguments into premake options. + RIVE_PREMAKE_FILE="${RIVE_PREMAKE_FILE:-./premake5.lua}" + if [ ! -f "$RIVE_PREMAKE_FILE" ]; then + echo "Premake file "$RIVE_PREMAKE_FILE" not found" + exit -1 + fi + + # Only use default arguments if RIVE_PREMAKE_ARGS is unset (not just empty). + if [ -z "${RIVE_PREMAKE_ARGS+null_detector_string}" ]; then + RIVE_PREMAKE_ARGS="--with_rive_text --with_rive_layout" + fi + + while [[ $# -gt 0 ]]; do + case "$1" in + "release") RIVE_CONFIG="${RIVE_CONFIG:-release}" ;; + "ios") RIVE_OS="${RIVE_OS:-ios}" ;; + "iossim") + RIVE_OS=${RIVE_OS:-ios} + RIVE_VARIANT="${RIVE_VARIANT:-emulator}" + RIVE_ARCH="${RIVE_ARCH:-universal}" # The simulator requires universal builds. + ;; + "android") RIVE_OS="${RIVE_OS:-android}" ;; + "arm") RIVE_ARCH="${RIVE_ARCH:-arm}" ;; + "arm64") RIVE_ARCH="${RIVE_ARCH:-arm64}" ;; + "x64") RIVE_ARCH="${RIVE_ARCH:-x64}" ;; + "universal") RIVE_ARCH="${RIVE_ARCH:-universal}" ;; + "wasm") RIVE_ARCH="${RIVE_ARCH:-wasm}" ;; + "ninja") RIVE_BUILD_SYSTEM="${RIVE_BUILD_SYSTEM:-ninja}" ;; + "xcode") RIVE_BUILD_SYSTEM="${RIVE_BUILD_SYSTEM:-xcode4}" ;; + "clean") RIVE_CLEAN="${RIVE_CLEAN:-true}" ;; + "compdb") + RIVE_BUILD_SYSTEM="${RIVE_BUILD_SYSTEM:-export-compile-commands}" + RIVE_OUT="${RIVE_OUT:-out/compdb}" + ;; + "--") + shift + break + ;; + *) RIVE_PREMAKE_ARGS="$RIVE_PREMAKE_ARGS $1" ;; + esac + shift + done + + # Android requires an architecture. Default to arm64 if not specified. + if [[ $RIVE_OS = "android" ]]; then + RIVE_ARCH="${RIVE_ARCH:-arm64}" + fi + + RIVE_CONFIG="${RIVE_CONFIG:-debug}" + + if [ -z "$RIVE_OUT" ]; then + RIVE_OUT="$RIVE_CONFIG" + if [ ! -z "$RIVE_ARCH" ]; then + RIVE_OUT="${RIVE_ARCH}_$RIVE_OUT" + fi + if [[ $RIVE_VARIANT = "emulator" ]]; then + RIVE_OUT="${RIVE_OS}sim_$RIVE_OUT" + elif [ ! -z "$RIVE_OS" ]; then + RIVE_OUT="${RIVE_OS}_$RIVE_OUT" + fi + RIVE_OUT="out/$RIVE_OUT" + fi + + if [[ "$HOST_MACHINE" = "windows" ]]; then + RIVE_BUILD_SYSTEM="${RIVE_BUILD_SYSTEM:-vs2022}" + else + RIVE_BUILD_SYSTEM="${RIVE_BUILD_SYSTEM:-gmake2}" + fi + + RIVE_PREMAKE_ARGS="$RIVE_BUILD_SYSTEM --file=$RIVE_PREMAKE_FILE --config=$RIVE_CONFIG --out=$RIVE_OUT $RIVE_PREMAKE_ARGS" + if [ ! -z "$RIVE_OS" ]; then RIVE_PREMAKE_ARGS="$RIVE_PREMAKE_ARGS --os=$RIVE_OS"; fi + if [ ! -z "$RIVE_VARIANT" ]; then RIVE_PREMAKE_ARGS="$RIVE_PREMAKE_ARGS --variant=$RIVE_VARIANT"; fi + if [ ! -z "$RIVE_ARCH" ]; then RIVE_PREMAKE_ARGS="$RIVE_PREMAKE_ARGS --arch=$RIVE_ARCH"; fi + + if [[ "$RIVE_CLEAN" = true ]]; then + rm -fr "./$RIVE_OUT" + fi +fi + +mkdir -p "$SCRIPT_DIR/dependencies" +pushd "$SCRIPT_DIR/dependencies" > /dev/null + +# Setup premake5. +if [ ! -d premake-core ]; then + echo Building Premake... + git clone https://github.com/premake/premake-core.git + pushd premake-core > /dev/null + case "$HOST_MACHINE" in + mac_arm64) make -f Bootstrap.mak osx PLATFORM=ARM ;; + mac_x64) make -f Bootstrap.mak osx ;; + windows) ./Bootstrap.bat ;; + *) make -f Bootstrap.mak linux ;; + esac + popd > /dev/null +fi +export PATH="$SCRIPT_DIR/dependencies/premake-core/bin/release/:$PATH" +export PREMAKE_PATH="$SCRIPT_DIR" + +# Setup premake-ninja. +if [[ $RIVE_BUILD_SYSTEM = "ninja" ]]; then + if [ ! -d premake-ninja ]; then + git clone --branch rive_modifications https://github.com/rive-app/premake-ninja.git + fi + export PREMAKE_PATH="$SCRIPT_DIR/dependencies/premake-ninja:$PREMAKE_PATH" +fi + +# Setup premake-export-compile-commands. +if [[ $RIVE_BUILD_SYSTEM = "export-compile-commands" ]]; then + if [ ! -d premake-export-compile-commands ]; then + git clone --branch more_cpp_support https://github.com/rive-app/premake-export-compile-commands.git + fi + export PREMAKE_PATH="$SCRIPT_DIR/dependencies/premake-export-compile-commands:$PREMAKE_PATH" +fi + +# Setup emscripten. +if [[ $RIVE_ARCH = "wasm" ]]; then + if [ ! -d emsdk ]; then + git clone https://github.com/emscripten-core/emsdk.git + emsdk/emsdk install 3.1.61 + emsdk/emsdk activate 3.1.61 + fi + source emsdk/emsdk_env.sh +fi + +popd > /dev/null # leave "$SCRIPT_DIR/dependencies" + +if [ -d "$RIVE_OUT" ]; then + if [[ "$RIVE_PREMAKE_ARGS" != "$(< $RIVE_OUT/.rive_premake_args)" ]]; then + echo "error: premake5 arguments for current build do not match previous arguments" + echo " previous command: premake5 $(< $RIVE_OUT/.rive_premake_args)" + echo " current command: premake5 $RIVE_PREMAKE_ARGS" + echo "If you wish to overwrite the existing build, please use 'clean'" + exit -1 + fi +else + mkdir -p "$RIVE_OUT" + echo "$RIVE_PREMAKE_ARGS" > "$RIVE_OUT/.rive_premake_args" +fi + +echo premake5 $RIVE_PREMAKE_ARGS +premake5 $RIVE_PREMAKE_ARGS | grep -v '^Done ([1-9]*ms).$' + +case "$RIVE_BUILD_SYSTEM" in + export-compile-commands) + rm -f ./compile_commands.json + cp $RIVE_OUT/compile_commands/default.json compile_commands.json + wc ./compile_commands.json + ;; + gmake2) + echo make -C $RIVE_OUT -j$NUM_CORES $@ + make -C $RIVE_OUT -j$NUM_CORES $@ + ;; + ninja) + echo ninja -C $RIVE_OUT $@ + ninja -C $RIVE_OUT $@ + ;; + xcode4) + if [[ $# = 0 ]]; then + echo 'No targets specified for xcode: Attempting to grok them from "xcodebuild -list".' + XCODE_SCHEMES=$(for f in $(xcodebuild -list -workspace $RIVE_OUT/rive.xcworkspace | grep '^ '); do printf " $f"; done) + echo " -> grokked:$XCODE_SCHEMES" + else + XCODE_SCHEMES="$@" + fi + for SCHEME in $XCODE_SCHEMES; do + echo xcodebuild -workspace $RIVE_OUT/rive.xcworkspace -scheme $SCHEME + xcodebuild -workspace $RIVE_OUT/rive.xcworkspace -scheme $SCHEME + done + ;; + vs2022) + for TARGET in $@; do + MSVC_TARGETS="$MSVC_TARGETS -t:$TARGET" + done + echo msbuild.exe "./$RIVE_OUT/rive.sln" $MSVC_TARGETS + msbuild.exe "./$RIVE_OUT/rive.sln" $MSVC_TARGETS + ;; + *) + echo "Unsupported buildsystem $RIVE_BUILD_SYSTEM" + exit -1 + ;; +esac diff --git a/build/dependency.lua b/build/dependency.lua index fe609d25..95f68269 100644 --- a/build/dependency.lua +++ b/build/dependency.lua @@ -42,7 +42,7 @@ function m.github(project, tag) and progress, } ) - print('Downloaded ' .. project .. '.') + print('Downloaded ' .. project .. ' to ' .. dependencies .. '/' .. hash) zip.extract(downloadFilename, dependencies .. '/' .. hash) os.remove(downloadFilename) end @@ -50,8 +50,6 @@ function m.github(project, tag) local iter = pairs(dirs) local currentKey, currentValue = iter(dirs) - print('Dependency ' .. project .. ' located at:') - print(' ' .. currentValue) return currentValue end return m diff --git a/build/premake5.lua b/build/premake5.lua index 4c34b540..66c7d20b 100644 --- a/build/premake5.lua +++ b/build/premake5.lua @@ -11,20 +11,30 @@ filter({ 'options:with_rive_text' }) do defines({ 'WITH_RIVE_TEXT' }) end -filter({}) filter({ 'options:with_rive_audio=system' }) do - defines({ 'WITH_RIVE_AUDIO' }) + defines({ 'WITH_RIVE_AUDIO', 'MA_NO_RESOURCE_MANAGER' }) end + filter({ 'options:with_rive_audio=external' }) do - defines({ 'WITH_RIVE_AUDIO', 'EXTERNAL_RIVE_AUDIO_ENGINE', 'MA_NO_DEVICE_IO' }) + defines({ + 'WITH_RIVE_AUDIO', + 'EXTERNAL_RIVE_AUDIO_ENGINE', + 'MA_NO_DEVICE_IO', + 'MA_NO_RESOURCE_MANAGER', + }) +end +filter({ 'options:with_rive_layout' }) +do + defines({ 'WITH_RIVE_LAYOUT' }) end filter({}) dofile(path.join(path.getabsolute('../dependencies/'), 'premake5_harfbuzz.lua')) dofile(path.join(path.getabsolute('../dependencies/'), 'premake5_sheenbidi.lua')) dofile(path.join(path.getabsolute('../dependencies/'), 'premake5_miniaudio.lua')) +dofile(path.join(path.getabsolute('../dependencies/'), 'premake5_yoga.lua')) project('rive') do @@ -38,8 +48,11 @@ do harfbuzz .. '/src', sheenbidi .. '/Headers', miniaudio, + yoga, }) + defines({ 'YOGA_EXPORT=' }) + files({ '../src/**.cpp' }) flags({ 'FatalCompileWarnings' }) @@ -115,6 +128,11 @@ do objdir('%{cfg.system}_sim/obj/%{cfg.buildcfg}') end + filter('system:macosx or system:ios') + do + files({ '../src/text/font_hb_apple.mm' }) + end + filter({ 'system:android', 'configurations:release' }) do buildoptions({ '-flto=full' }) @@ -150,11 +168,6 @@ do objdir('%{cfg.system}/arm64/obj/%{cfg.buildcfg}') end - filter('system:emscripten') - do - buildoptions({ '-pthread' }) - end - filter('configurations:debug') do defines({ 'DEBUG' }) @@ -200,3 +213,8 @@ newoption({ description = 'The audio mode to use.', allowed = { { 'disabled' }, { 'system' }, { 'external' } }, }) + +newoption({ + trigger = 'with_rive_layout', + description = 'Compiles in layout features.', +}) diff --git a/build/rive_build_config.lua b/build/rive_build_config.lua index 25f9c3d9..9e2a7bf1 100644 --- a/build/rive_build_config.lua +++ b/build/rive_build_config.lua @@ -1,3 +1,11 @@ +if _ACTION == 'ninja' then + require('ninja') +end + +if _ACTION == 'export-compile-commands' then + require('export-compile-commands') +end + workspace('rive') newoption({ @@ -23,7 +31,7 @@ newoption({ description = 'Directory to generate build files', default = nil, }) -RIVE_BUILD_OUT = _OPTIONS['out'] or ('out/' .. RIVE_BUILD_CONFIG) +RIVE_BUILD_OUT = _WORKING_DIR .. '/' .. (_OPTIONS['out'] or ('out/' .. RIVE_BUILD_CONFIG)) newoption({ trigger = 'toolset', @@ -69,11 +77,21 @@ newoption({ description = 'don\'t disable rtti (nonstandard for Rive)', }) +newoption({ + trigger = 'with-pic', + description = 'enable position independent code', +}) + newoption({ trigger = 'with-exceptions', description = 'don\'t disable exceptions (nonstandard for Rive)', }) +newoption({ + trigger = 'no-wasm-simd', + description = 'disable simd for wasm builds', +}) + newoption({ trigger = 'wasm_single', description = 'Embed wasm directly into the js, instead of side-loading it.', @@ -84,9 +102,9 @@ newoption({ description = 'Don\'t build with link time optimizations.', }) -location(_WORKING_DIR .. '/' .. RIVE_BUILD_OUT) -targetdir(_WORKING_DIR .. '/' .. RIVE_BUILD_OUT) -objdir(_WORKING_DIR .. '/' .. RIVE_BUILD_OUT .. '/obj') +location(RIVE_BUILD_OUT) +targetdir(RIVE_BUILD_OUT) +objdir(RIVE_BUILD_OUT .. '/obj') toolset(_OPTIONS['toolset'] or 'clang') language('C++') cppdialect('C++17') @@ -100,6 +118,13 @@ exceptionhandling('Off') filter({ 'options:with-exceptions' }) exceptionhandling('On') +filter({ 'options:with-pic' }) +do + pic('on') + buildoptions({ '-fPIC' }) + linkoptions({ '-fPIC' }) +end + filter('options:config=debug') do defines({ 'DEBUG' }) @@ -113,7 +138,7 @@ do optimize('On') end -filter({ 'options:config=release', 'options:not no-lto', 'system:not macosx', 'system:not ios'}) +filter({ 'options:config=release', 'options:not no-lto', 'system:not macosx', 'system:not ios' }) do flags({ 'LinkTimeOptimization' }) end @@ -125,12 +150,63 @@ do linkoptions({ '-flto=full' }) end -filter('system:windows') +newoption({ + trigger = 'windows_runtime', + description = 'Choose whether to use staticruntime on/off/default', + allowed = { + { 'default', 'Use default runtime' }, + { 'static', 'Use static runtime' }, + { 'dynamic', 'Use dynamic runtime' }, + { 'dynamic_debug', 'Use dynamic runtime force debug' }, + { 'dynamic_release', 'Use dynamic runtime force release' }, + }, + default = 'default', +}) + +-- This is just to match our old windows config. Rive Native specifically sets +-- static/dynamic and maybe we should do the same elsewhere. +filter({ 'system:windows', 'options:windows_runtime=default' }) do staticruntime('on') -- Match Skia's /MT flag for link compatibility - runtime('Release') -- Use /MT even in debug (/MTd is incompatible with Skia) + runtime('Release') +end + +filter({ 'system:windows', 'options:windows_runtime=static' }) +do + staticruntime('on') -- Match Skia's /MT flag for link compatibility +end + +filter({ 'system:windows', 'options:windows_runtime=dynamic' }) +do + staticruntime('off') +end + +filter({ 'system:windows', 'options:not windows_runtime=default', 'options:config=debug' }) +do + runtime('Debug') +end + +filter({ 'system:windows', 'options:not windows_runtime=default', 'options:config=release' }) +do + runtime('Release') +end + +filter({ 'system:windows', 'options:windows_runtime=dynamic_debug' }) +do + staticruntime('off') + runtime('Debug') +end + +filter({ 'system:windows', 'options:windows_runtime=dynamic_release' }) +do + staticruntime('off') + runtime('Release') +end + +filter('system:windows') +do architecture('x64') - defines({ '_USE_MATH_DEFINES' }) + defines({ '_USE_MATH_DEFINES', 'NOMINMAX' }) end filter({ 'system:windows', 'options:toolset=clang' }) @@ -216,6 +292,11 @@ do }) end +filter({ 'action:vs2022' }) +do + flags({ 'MultiProcessorCompile' }) +end + filter({}) -- Don't use filter() here because we don't want to generate the "android_ndk" toolset if not @@ -223,9 +304,9 @@ filter({}) if _OPTIONS['os'] == 'android' then pic('on') -- Position-independent code is required for NDK libraries. - local ndk = os.getenv('NDK_PATH') - if not ndk or ndk == '' then - error('export $NDK_PATH') + local ndk = os.getenv('NDK_PATH') or os.getenv('ANDROID_NDK') + if not ndk then + error('export $NDK_PATH or $ANDROID_NDK') end local ndk_toolchain = ndk .. '/toolchains/llvm/prebuilt' @@ -253,6 +334,8 @@ if _OPTIONS['os'] == 'android' then return android_ndk_tools[tool] end + valid_cc_tools = premake.action._list['gmake2'].valid_tools['cc'] + valid_cc_tools[#valid_cc_tools + 1] = 'android_ndk' toolset('android_ndk') buildoptions({ @@ -306,9 +389,37 @@ if _OPTIONS['os'] == 'android' then filter({}) end +filter('system:linux', 'options:arch=x64') +do + architecture('x64') +end + +filter('system:linux', 'options:arch=arm') +do + architecture('arm') +end + +filter('system:linux', 'options:arch=arm64') +do + architecture('arm64') +end + +filter({}) + if os.host() == 'macosx' then iphoneos_sysroot = os.outputof('xcrun --sdk iphoneos --show-sdk-path') + if iphoneos_sysroot == nil then + error( + 'Unable to locate iphoneos sdk. Please ensure Xcode Command Line Tools are installed.' + ) + end + iphonesimulator_sysroot = os.outputof('xcrun --sdk iphonesimulator --show-sdk-path') + if iphonesimulator_sysroot == nil then + error( + 'Unable to locate iphonesimulator sdk. Please ensure Xcode Command Line Tools are installed.' + ) + end filter('system:ios') do @@ -338,6 +449,21 @@ if os.host() == 'macosx' then buildoptions({ '-fobjc-arc' }) end + filter({ 'system:macosx' }) + do + buildoptions({ + '-mmacosx-version-min=11.0', + }) + end + + filter({ 'system:macosx', 'options:arch=host', 'action:xcode4' }) + do + -- xcode tries to specify a target... + buildoptions({ + '--target=' .. os.outputof('uname -m') .. '-apple-macos11.0', + }) + end + filter({ 'system:macosx', 'options:arch=arm64 or arch=universal' }) do buildoptions({ '-arch arm64' }) @@ -402,16 +528,40 @@ if _OPTIONS['arch'] == 'wasm' or _OPTIONS['arch'] == 'js' then end system('emscripten') + + valid_cc_tools = premake.action._list['gmake2'].valid_tools['cc'] + valid_cc_tools[#valid_cc_tools + 1] = 'emsdk' toolset('emsdk') linkoptions({ '-sALLOW_MEMORY_GROWTH=1', '-sDYNAMIC_EXECUTION=0' }) filter('options:arch=wasm') do - buildoptions({ '-msimd128' }) linkoptions({ '-sWASM=1' }) end + filter({ 'options:arch=wasm', 'options:not no-wasm-simd' }) + do + buildoptions({ '-msimd128' }) + end + + filter({ 'options:arch=wasm', 'options:no-wasm-simd' }) + do + linkoptions({ '-s MIN_SAFARI_VERSION=120000' }) + end + + filter({ 'options:arch=wasm', 'options:config=debug' }) + do + buildoptions({ + '-fsanitize=address', + '-g2', + }) + linkoptions({ + '-fsanitize=address', + '-g2', + }) + end + filter('options:arch=js') do linkoptions({ '-sWASM=0' }) @@ -424,7 +574,7 @@ if _OPTIONS['arch'] == 'wasm' or _OPTIONS['arch'] == 'js' then filter('options:config=release') do - optimize('Size') + buildoptions({ '-Oz' }) end filter({}) diff --git a/cg_renderer/premake5.lua b/cg_renderer/premake5.lua index d54c7e52..a4a60627 100644 --- a/cg_renderer/premake5.lua +++ b/cg_renderer/premake5.lua @@ -43,11 +43,4 @@ do }) end end - - filter({ 'options:with_rive_text' }) - do - defines({ 'WITH_RIVE_TEXT' }) - end end - -newoption({ trigger = 'with_rive_text', description = 'Enables text experiments' }) diff --git a/decoders/premake5_v2.lua b/decoders/premake5_v2.lua index b85d38b4..6c90d953 100644 --- a/decoders/premake5_v2.lua +++ b/decoders/premake5_v2.lua @@ -1,16 +1,45 @@ dofile('rive_build_config.lua') +if _OPTIONS['no-rive-decoders'] then + return +end + rive = path.getabsolute('../') dofile(rive .. '/dependencies/premake5_libpng_v2.lua') +dofile(rive .. '/dependencies/premake5_libjpeg_v2.lua') +dofile(rive .. '/dependencies/premake5_libwebp_v2.lua') project('rive_decoders') do - dependson('libpng') + dependson('libpng', 'zlib', 'libjpeg', 'libwebp') kind('StaticLib') flags({ 'FatalWarnings' }) - includedirs({ 'include', '../include', libpng }) + includedirs({ 'include', '../include', libpng, libjpeg, libwebp .. '/src' }) + + files({ 'src/bitmap_decoder.cpp' }) + + filter({ 'options:not no-libjpeg-renames' }) + do + includedirs({ + rive .. '/dependencies', + }) + forceincludes({ 'rive_libjpeg_renames.h' }) + end + + filter({ 'system:macosx or system:ios' }) + do + files({ 'src/**.mm' }) + end - files({ 'src/**.cpp' }) + filter({ 'system:not macosx', 'system:not ios' }) + do + files({ + 'src/bitmap_decoder_thirdparty.cpp', + 'src/decode_webp.cpp', + 'src/decode_jpeg.cpp', + 'src/decode_png.cpp', + }) + end end diff --git a/decoders/src/bitmap_decoder.cpp b/decoders/src/bitmap_decoder.cpp index 243ce29a..5bb4ee0a 100644 --- a/decoders/src/bitmap_decoder.cpp +++ b/decoders/src/bitmap_decoder.cpp @@ -38,66 +38,6 @@ size_t Bitmap::byteSize(PixelFormat format) const size_t Bitmap::byteSize() const { return byteSize(m_PixelFormat); } -std::unique_ptr DecodePng(const uint8_t bytes[], size_t byteCount); -std::unique_ptr DecodeJpeg(const uint8_t bytes[], size_t byteCount) { return nullptr; } -std::unique_ptr DecodeWebP(const uint8_t bytes[], size_t byteCount) { return nullptr; } - -using BitmapDecoder = std::unique_ptr (*)(const uint8_t bytes[], size_t byteCount); -struct ImageFormat -{ - const char* name; - std::vector fingerprint; - BitmapDecoder decodeImage; -}; - -std::unique_ptr Bitmap::decode(const uint8_t bytes[], size_t byteCount) -{ - static ImageFormat decoders[] = { - { - "png", - {0x89, 0x50, 0x4E, 0x47}, - DecodePng, - }, - { - "jpeg", - {0xFF, 0xD8, 0xFF}, - DecodeJpeg, - }, - { - "webp", - {0x52, 0x49, 0x46}, - DecodeWebP, - }, - }; - - for (auto recognizer : decoders) - { - auto& fingerprint = recognizer.fingerprint; - - // Immediately discard decoders with fingerprints that are longer than - // the file buffer. - if (recognizer.fingerprint.size() > byteCount) - { - continue; - } - - // If the fingerprint doesn't match, discrd this decoder. These are all bytes so .size() is - // fine here. - if (memcmp(fingerprint.data(), bytes, fingerprint.size()) != 0) - { - continue; - } - - auto bitmap = recognizer.decodeImage(bytes, byteCount); - if (!bitmap) - { - fprintf(stderr, "Bitmap::decode - failed to decode a %s.\n", recognizer.name); - } - return bitmap; - } - return nullptr; -} - void Bitmap::pixelFormat(PixelFormat format) { if (format == m_PixelFormat) diff --git a/decoders/src/bitmap_decoder_cg.mm b/decoders/src/bitmap_decoder_cg.mm new file mode 100644 index 00000000..82b15d6d --- /dev/null +++ b/decoders/src/bitmap_decoder_cg.mm @@ -0,0 +1,165 @@ +/* + * Copyright 2023 Rive + */ + +#include "rive/decoders/bitmap_decoder.hpp" +#include "rive/rive_types.hpp" +#include "rive/math/simd.hpp" +#include "rive/math/math_types.hpp" +#include "rive/core/type_conversions.hpp" +#include "utils/auto_cf.hpp" + +#include + +#if TARGET_OS_IPHONE +#include +#include +#elif TARGET_OS_MAC +#include +#endif + +#include +#include +#include + +// Represents raw, premultiplied, RGBA image data with tightly packed rows (width * 4 bytes). +struct PlatformCGImage +{ + uint32_t width = 0; + uint32_t height = 0; + bool opaque = false; + std::unique_ptr pixels; +}; + +bool cg_image_decode(const uint8_t* encodedBytes, + size_t encodedSizeInBytes, + PlatformCGImage* platformImage) +{ + AutoCF data = CFDataCreate(kCFAllocatorDefault, encodedBytes, encodedSizeInBytes); + if (!data) + { + return false; + } + + AutoCF source = CGImageSourceCreateWithData(data, nullptr); + if (!source) + { + return false; + } + + AutoCF image = CGImageSourceCreateImageAtIndex(source, 0, nullptr); + if (!image) + { + return false; + } + + bool isOpaque = false; + switch (CGImageGetAlphaInfo(image.get())) + { + case kCGImageAlphaNone: + case kCGImageAlphaNoneSkipFirst: + case kCGImageAlphaNoneSkipLast: + isOpaque = true; + break; + default: + break; + } + + const size_t width = CGImageGetWidth(image); + const size_t height = CGImageGetHeight(image); + const size_t rowBytes = width * 4; // 4 bytes per pixel + const size_t size = rowBytes * height; + + const size_t bitsPerComponent = 8; + CGBitmapInfo cgInfo = kCGBitmapByteOrder32Big; // rgba + if (isOpaque) + { + cgInfo |= kCGImageAlphaNoneSkipLast; + } + else + { + cgInfo |= kCGImageAlphaPremultipliedLast; + } + + std::unique_ptr pixels(new uint8_t[size]); + + AutoCF cs = CGColorSpaceCreateDeviceRGB(); + AutoCF cg = + CGBitmapContextCreate(pixels.get(), width, height, bitsPerComponent, rowBytes, cs, cgInfo); + if (!cg) + { + return false; + } + + CGContextSetBlendMode(cg, kCGBlendModeCopy); + CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image); + + platformImage->width = rive::castTo(width); + platformImage->height = rive::castTo(height); + platformImage->opaque = isOpaque; + platformImage->pixels = std::move(pixels); + + return true; +} + +std::unique_ptr Bitmap::decode(const uint8_t bytes[], size_t byteCount) +{ + PlatformCGImage image; + if (!cg_image_decode(bytes, byteCount, &image)) + { + return nullptr; + } + + // CG only supports premultiplied alpha. Unmultiply now. + size_t imageNumPixels = image.height * image.width; + size_t imageSizeInBytes = imageNumPixels * 4; + // Process 2 pixels at once, deal with odd number of pixels + if (imageNumPixels & 1) + { + imageSizeInBytes -= 4; + } + size_t i; + for (i = 0; i < imageSizeInBytes; i += 8) + { + // Load 2 pixels into 64 bits + auto twoPixels = rive::simd::load(&image.pixels[i]); + auto a0 = twoPixels[3]; + auto a1 = twoPixels[7]; + // Avoid computation if both pixels are either fully transparent or opaque pixels + if ((a0 > 0 && a0 < 255) || (a1 > 0 && a1 < 255)) + { + // Avoid potential division by zero + a0 = std::max(a0, 1); + a1 = std::max(a1, 1); + // Cast to 16 bits to avoid overflow + rive::uint16x8 rgbaWidex2 = rive::simd::cast(twoPixels); + // Unpremult: multiply by RGB by "255.0 / alpha" + rgbaWidex2 *= rive::uint16x8{255, 255, 255, 1, 255, 255, 255, 1}; + rgbaWidex2 /= rive::uint16x8{a0, a0, a0, 1, a1, a1, a1, 1}; + // Cast back to 8 bits and store + twoPixels = rive::simd::cast(rgbaWidex2); + rive::simd::store(&image.pixels[i], twoPixels); + } + } + // Process last odd pixel if needed + if (imageNumPixels & 1) + { + // Load 1 pixel into 32 bits + auto rgba = rive::simd::load(&image.pixels[i]); + // Avoid computation for fully transparent or opaque pixels + if (rgba.a > 0 && rgba.a < 255) + { + // Cast to 16 bits to avoid overflow + rive::uint16x4 rgbaWide = rive::simd::cast(rgba); + // Unpremult: multiply by RGB by "255.0 / alpha" + rgbaWide *= rive::uint16x4{255, 255, 255, 1}; + rgbaWide /= rive::uint16x4{rgba.a, rgba.a, rgba.a, 1}; + // Cast back to 8 bits and store + rgba = rive::simd::cast(rgbaWide); + rive::simd::store(&image.pixels[i], rgba); + } + } + + return std::make_unique( + image.width, image.height, PixelFormat::RGBA, std::move(image.pixels)); +} diff --git a/decoders/src/bitmap_decoder_thirdparty.cpp b/decoders/src/bitmap_decoder_thirdparty.cpp new file mode 100644 index 00000000..37c5a667 --- /dev/null +++ b/decoders/src/bitmap_decoder_thirdparty.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2023 Rive + */ + +#include "rive/decoders/bitmap_decoder.hpp" +#include "rive/rive_types.hpp" +#include +#include +#include + +std::unique_ptr DecodePng(const uint8_t bytes[], size_t byteCount); +std::unique_ptr DecodeJpeg(const uint8_t bytes[], size_t byteCount); +std::unique_ptr DecodeWebP(const uint8_t bytes[], size_t byteCount); + +using BitmapDecoder = std::unique_ptr (*)(const uint8_t bytes[], size_t byteCount); +struct ImageFormat +{ + const char* name; + std::vector fingerprint; + BitmapDecoder decodeImage; +}; + +std::unique_ptr Bitmap::decode(const uint8_t bytes[], size_t byteCount) +{ + static ImageFormat decoders[] = { + { + "png", + {0x89, 0x50, 0x4E, 0x47}, + DecodePng, + }, + { + "jpeg", + {0xFF, 0xD8, 0xFF}, + DecodeJpeg, + }, + { + "webp", + {0x52, 0x49, 0x46}, + DecodeWebP, + }, + }; + + for (auto recognizer : decoders) + { + auto& fingerprint = recognizer.fingerprint; + + // Immediately discard decoders with fingerprints that are longer than + // the file buffer. + if (recognizer.fingerprint.size() > byteCount) + { + continue; + } + + // If the fingerprint doesn't match, discrd this decoder. These are all bytes so .size() is + // fine here. + if (memcmp(fingerprint.data(), bytes, fingerprint.size()) != 0) + { + continue; + } + + auto bitmap = recognizer.decodeImage(bytes, byteCount); + if (!bitmap) + { + fprintf(stderr, "Bitmap::decode - failed to decode a %s.\n", recognizer.name); + } + return bitmap; + } + return nullptr; +} diff --git a/decoders/src/decode_jpeg.cpp b/decoders/src/decode_jpeg.cpp new file mode 100644 index 00000000..2ed8c9ae --- /dev/null +++ b/decoders/src/decode_jpeg.cpp @@ -0,0 +1,127 @@ +// Adapted from libjpeg-turbo's example: +// https://github.com/libjpeg-turbo/libjpeg-turbo/blob/main/example.c +#include "rive/decoders/bitmap_decoder.hpp" + +#include "jpeglib.h" +#include "jerror.h" + +#include +#include +#include +#include + +struct my_error_mgr +{ + struct jpeg_error_mgr pub; + jmp_buf setjmp_buffer; +}; + +typedef struct my_error_mgr* my_error_ptr; + +void my_error_exit(j_common_ptr cinfo) +{ + // cinfo.err really points to a my_error_mgr struct, so coerce pointer + my_error_ptr myerr = (my_error_ptr)cinfo->err; + + // Always display the message. + // We could postpone this until after returning, if we chose. + (*cinfo->err->output_message)(cinfo); + + // Return control to the setjmp point + longjmp(myerr->setjmp_buffer, 1); +} + +std::unique_ptr DecodeJpeg(const uint8_t bytes[], size_t byteCount) +{ + struct jpeg_decompress_struct cinfo; + struct my_error_mgr jerr; + + JSAMPARRAY buffer = nullptr; + std::unique_ptr pixelBuffer; + int row_stride; + + // Step 1: allocate and initialize JPEG decompression object. + + // We set up the normal JPEG error routines, then override error_exit. + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = my_error_exit; + // Establish the setjmp return context for my_error_exit to use. + if (setjmp(jerr.setjmp_buffer)) + { + // If we get here, the JPEG code has signaled an error. + // We need to clean up the JPEG object, close the input file, and return. + jpeg_destroy_decompress(&cinfo); + return nullptr; + } + + // Now we can initialize the JPEG decompression object. + jpeg_create_decompress(&cinfo); + + // Step 2: specify data source + jpeg_mem_src(&cinfo, bytes, byteCount); + + // Step 3: read file parameters with jpeg_read_header() + + jpeg_read_header(&cinfo, TRUE); + + // Step 4: set parameters for decompression + + // always want 8 bit RGB + cinfo.data_precision = 8; + cinfo.out_color_space = JCS_RGB; + + // Step 5: Start decompressor + jpeg_start_decompress(&cinfo); + + /// Api worked as expected and gave us correct format even for jpeg 12 or 16 + assert(cinfo.data_precision == 8); + assert(cinfo.output_components == 3); + + size_t pixelBufferSize = static_cast(cinfo.output_width) * + static_cast(cinfo.output_height) * + static_cast(cinfo.output_components); + pixelBuffer = std::make_unique(pixelBufferSize); + + uint8_t* pixelWriteBuffer = (uint8_t*)pixelBuffer.get(); + const uint8_t* pixelWriteBufferEnd = pixelWriteBuffer + pixelBufferSize; + + // Samples per row in output buffer + row_stride = cinfo.output_width * cinfo.output_components; + // Make a one-row-high sample array that will go away when done with image + buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1); + + // Step 6: while (scan lines remain to be read) + // jpeg_read_scanlines(...); + + // Here we use the library's state variable cinfo->output_scanline as the + // loop counter, so that we don't have to keep track ourselves. + // + while (cinfo.output_scanline < cinfo.output_height) + { + // jpeg_read_scanlines expects an array of pointers to scanlines. + // Here the array is only one element long, but you could ask for + // more than one scanline at a time if that's more convenient. + jpeg_read_scanlines(&cinfo, buffer, 1); + + if (pixelWriteBuffer + row_stride > pixelWriteBufferEnd) + { + // memcpy would cause an overflow. + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + return nullptr; + } + memcpy(pixelWriteBuffer, buffer[0], row_stride); + pixelWriteBuffer += row_stride; + } + + // Step 7: Finish decompression + jpeg_finish_decompress(&cinfo); + + // Step 8: Release JPEG decompression object + jpeg_destroy_decompress(&cinfo); + + return std::make_unique(cinfo.output_width, + cinfo.output_height, + Bitmap::PixelFormat::RGB, + std::move(pixelBuffer)); +} \ No newline at end of file diff --git a/decoders/src/decode_png.cpp b/decoders/src/decode_png.cpp index edfb1bba..fcf441ca 100644 --- a/decoders/src/decode_png.cpp +++ b/decoders/src/decode_png.cpp @@ -7,6 +7,7 @@ #include #include #include +#include struct EncodedImageBuffer { @@ -40,6 +41,8 @@ std::unique_ptr DecodePng(const uint8_t bytes[], size_t byteCount) png_infop info_ptr; png_uint_32 width, height; int bit_depth, color_type, interlace_type; + std::unique_ptr pixelBuffer; + std::unique_ptr rowsPointer; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); @@ -57,6 +60,12 @@ std::unique_ptr DecodePng(const uint8_t bytes[], size_t byteCount) return nullptr; } + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return nullptr; + } + EncodedImageBuffer stream = {bytes, 0, byteCount}; png_set_read_fn(png_ptr, &stream, ReadDataFromMemory); @@ -101,22 +110,30 @@ std::unique_ptr DecodePng(const uint8_t bytes[], size_t byteCount) png_read_update_info(png_ptr, info_ptr); uint8_t channels = png_get_channels(png_ptr, info_ptr); - auto pixelBuffer = new uint8_t[width * height * channels]; - - png_bytep* row_pointers = new png_bytep[height]; + size_t pixelBufferSize = + static_cast(width) * static_cast(height) * static_cast(channels); + pixelBuffer = std::make_unique(pixelBufferSize); + const uint8_t* pixelBufferEnd = pixelBuffer.get() + pixelBufferSize; - for (unsigned row = 0; row < height; row++) + rowsPointer = std::make_unique(height); + png_bytep* rows = rowsPointer.get(); + size_t rowStride = (size_t)width * (size_t)channels; + uint8_t* rowWrite = pixelBuffer.get(); + for (png_uint_32 row = 0; row < height; row++) { - unsigned int rIndex = row; - row_pointers[rIndex] = pixelBuffer + (row * (width * channels)); + rows[row] = rowWrite; + rowWrite += rowStride; + if (rowWrite > pixelBufferEnd) + { + // Read would overflow. + return nullptr; + } } - png_read_image(png_ptr, row_pointers); + png_read_image(png_ptr, rows); png_read_end(png_ptr, info_ptr); png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) nullptr); - delete[] row_pointers; - Bitmap::PixelFormat pixelFormat; assert(channels == 3 || channels == 4); switch (channels) @@ -128,5 +145,5 @@ std::unique_ptr DecodePng(const uint8_t bytes[], size_t byteCount) pixelFormat = Bitmap::PixelFormat::RGB; break; } - return std::make_unique(width, height, pixelFormat, pixelBuffer); + return std::make_unique(width, height, pixelFormat, std::move(pixelBuffer)); } diff --git a/decoders/src/decode_webp.cpp b/decoders/src/decode_webp.cpp new file mode 100644 index 00000000..f2617982 --- /dev/null +++ b/decoders/src/decode_webp.cpp @@ -0,0 +1,68 @@ +#include "rive/decoders/bitmap_decoder.hpp" +#include "webp/decode.h" +#include "webp/demux.h" +#include +#include +#include + +std::unique_ptr DecodeWebP(const uint8_t bytes[], size_t byteCount) +{ + WebPDecoderConfig config; + if (!WebPInitDecoderConfig(&config)) + { + fprintf(stderr, "DecodeWebP - Library version mismatch!\n"); + return nullptr; + } + config.options.dithering_strength = 50; + config.options.alpha_dithering_strength = 100; + + if (!WebPGetInfo(bytes, byteCount, nullptr, nullptr)) + { + fprintf(stderr, "DecodeWebP - Input file doesn't appear to be WebP format.\n"); + } + + WebPData data = {bytes, byteCount}; + WebPDemuxer* demuxer = WebPDemux(&data); + if (demuxer == nullptr) + { + fprintf(stderr, "DecodeWebP - Could not create demuxer.\n"); + } + + WebPIterator currentFrame; + if (!WebPDemuxGetFrame(demuxer, 1, ¤tFrame)) + { + fprintf(stderr, "DecodeWebP - WebPDemuxGetFrame couldn't get frame.\n"); + WebPDemuxDelete(demuxer); + return nullptr; + } + config.output.colorspace = MODE_RGBA; + + uint32_t width = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH); + uint32_t height = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT); + + size_t pixelBufferSize = + static_cast(width) * static_cast(height) * static_cast(4); + std::unique_ptr pixelBuffer = std::make_unique(pixelBufferSize); + + config.output.u.RGBA.rgba = (uint8_t*)pixelBuffer.get(); + config.output.u.RGBA.stride = (int)(width * 4); + config.output.u.RGBA.size = pixelBufferSize; + config.output.is_external_memory = 1; + + if (WebPDecode(currentFrame.fragment.bytes, currentFrame.fragment.size, &config) != + VP8_STATUS_OK) + { + fprintf(stderr, "DecodeWebP - WebPDemuxGetFrame couldn't decode.\n"); + WebPDemuxReleaseIterator(¤tFrame); + WebPDemuxDelete(demuxer); + return nullptr; + } + + WebPDemuxReleaseIterator(¤tFrame); + WebPDemuxDelete(demuxer); + + return std::make_unique(width, + height, + Bitmap::PixelFormat::RGBA, + std::move(pixelBuffer)); +} \ No newline at end of file diff --git a/dependencies/gen_libjpeg_renames/gen_header.dart b/dependencies/gen_libjpeg_renames/gen_header.dart new file mode 100644 index 00000000..59cc2135 --- /dev/null +++ b/dependencies/gen_libjpeg_renames/gen_header.dart @@ -0,0 +1,74 @@ +import 'dart:collection'; +import 'dart:io'; + +final skip = HashSet.from( + [], +); + +final extras = HashSet.from( + [ + 'read_quant_tables', + 'read_scan_script', + 'set_quality_ratings', + 'set_quant_slots', + 'set_sample_factors', + 'read_color_map', + 'enable_signal_catcher', + 'start_progress_monitor', + 'end_progress_monitor', + 'read_stdin', + 'write_stdout', + 'jdiv_round_up', + 'jround_up', + 'jzero_far', + 'jcopy_sample_rows', + 'jcopy_block_row', + 'jtransform_parse_crop_spec', + 'jtransform_request_workspace', + 'jtransform_adjust_parameters', + 'jtransform_execute_transform', + 'jtransform_perfect_transform', + 'jcopy_markers_setup', + 'jcopy_markers_execute', + ], +); + +void main() { + final uniqueNames = HashSet(); + var header = StringBuffer(); + header.writeln('// clang-format off'); + header.writeln('// jpeg_*'); + var contents = File('libjpeg_names.txt').readAsStringSync(); + RegExp exp = RegExp(r'\s_(jpeg_([a-zA-Z0-9_]*))', multiLine: true); + Iterable matches = exp.allMatches(contents); + for (final m in matches) { + var symbolName = m[1]; + if (symbolName == null || + skip.contains(symbolName) || + uniqueNames.contains(symbolName)) { + continue; + } + uniqueNames.add(symbolName); + header.writeln('#define $symbolName rive_$symbolName'); + } + header.writeln('// jinit_*'); + { + RegExp exp = RegExp(r'\s_(jinit_([a-zA-Z0-9_]*))', multiLine: true); + Iterable matches = exp.allMatches(contents); + for (final m in matches) { + var symbolName = m[1]; + if (symbolName == null || + skip.contains(symbolName) || + uniqueNames.contains(symbolName)) { + continue; + } + uniqueNames.add(symbolName); + header.writeln('#define $symbolName rive_$symbolName'); + } + } + header.writeln('// _j extras'); + for (final symbolName in extras) { + header.writeln('#define $symbolName rive_$symbolName'); + } + File('../rive_libjpeg_renames.h').writeAsStringSync(header.toString()); +} diff --git a/dependencies/gen_libjpeg_renames/gen_renames.sh b/dependencies/gen_libjpeg_renames/gen_renames.sh new file mode 100755 index 00000000..6f45c0bf --- /dev/null +++ b/dependencies/gen_libjpeg_renames/gen_renames.sh @@ -0,0 +1,58 @@ +#!/bin/bash +set -e + +# This script should be called on a Mac! + +# NOTE: Before building, jconfig.txt needs to be renamed to jconfig.h + +if [[ ! -f "dependencies/bin/premake5" ]]; then + mkdir -p dependencies/bin + pushd dependencies + # v5.0.0-beta2 doesn't support apple silicon properly, update the branch + # once a stable one is avaialble that supports it + git clone --depth 1 --branch master https://github.com/premake/premake-core.git + pushd premake-core + if [[ $LOCAL_ARCH == "arm64" ]]; then + PREMAKE_MAKE_ARCH=ARM + else + PREMAKE_MAKE_ARCH=x86 + fi + make -f Bootstrap.mak osx PLATFORM=$PREMAKE_MAKE_ARCH + cp bin/release/* ../bin + popd + popd +fi + +export PREMAKE=$PWD/dependencies/bin/premake5 + +for var in "$@"; do + if [[ $var = "clean" ]]; then + echo 'Cleaning...' + rm -fR out + fi +done + +mkdir -p out +mkdir -p out_with_renames + +pushd ../../../ +PACKAGES=$PWD +popd +export PREMAKE_PATH="dependencies/export-compile-commands":"$PACKAGES/runtime_unity/native_plugin/platform":"$PACKAGES/runtime/build":"$PREMAKE_PATH" + +$PREMAKE gmake2 --file=../premake5_libjpeg_v2.lua --out=out --no-libjpeg-renames +pushd out +make -j$(($(sysctl -n hw.physicalcpu) + 1)) +popd + +nm out/liblibjpeg.a --demangle &>libjpeg_names.txt + +dart gen_header.dart + +# make with renames just to examine the exported symbols... +$PREMAKE gmake2 --file=../premake5_libjpeg_v2.lua --out=out_with_renames +pushd out_with_renames +make -j$(($(sysctl -n hw.physicalcpu) + 1)) +popd + +nm out_with_renames/liblibjpeg.a --demangle &>libjpeg_renames.txt diff --git a/dependencies/gen_yoga_renames/gen_header.dart b/dependencies/gen_yoga_renames/gen_header.dart new file mode 100644 index 00000000..d5f724db --- /dev/null +++ b/dependencies/gen_yoga_renames/gen_header.dart @@ -0,0 +1,46 @@ +import 'dart:collection'; +import 'dart:io'; + +final skip = HashSet.from( + [], +); + +final extras = HashSet.from( + [], +); + +void main() { + final uniqueNames = HashSet(); + var header = StringBuffer(); + header.writeln('// clang-format off'); + header.writeln('// YG*'); + var contents = File('yoga_names.txt').readAsStringSync(); + RegExp exp = RegExp(r'\s(YG([a-zA-Z0-9_]*))', multiLine: true); + Iterable matches = exp.allMatches(contents); + for (final m in matches) { + var symbolName = m[1]; + if (symbolName == null || + skip.contains(symbolName) || + uniqueNames.contains(symbolName)) { + continue; + } + uniqueNames.add(symbolName); + header.writeln('#define $symbolName rive_$symbolName'); + } + header.writeln('// _YG*'); + { + RegExp exp = RegExp(r'\s_(YG([a-zA-Z0-9_]*))', multiLine: true); + Iterable matches = exp.allMatches(contents); + for (final m in matches) { + var symbolName = m[1]; + if (symbolName == null || + skip.contains(symbolName) || + uniqueNames.contains(symbolName)) { + continue; + } + uniqueNames.add(symbolName); + header.writeln('#define $symbolName rive_$symbolName'); + } + } + File('../rive_yoga_renames.h').writeAsStringSync(header.toString()); +} diff --git a/dependencies/gen_yoga_renames/gen_renames.sh b/dependencies/gen_yoga_renames/gen_renames.sh new file mode 100755 index 00000000..eadf7d7c --- /dev/null +++ b/dependencies/gen_yoga_renames/gen_renames.sh @@ -0,0 +1,56 @@ +#!/bin/bash +set -e + +# This script should be called on a Mac! + +if [[ ! -f "dependencies/bin/premake5" ]]; then + mkdir -p dependencies/bin + pushd dependencies + # v5.0.0-beta2 doesn't support apple silicon properly, update the branch + # once a stable one is avaialble that supports it + git clone --depth 1 --branch master https://github.com/premake/premake-core.git + pushd premake-core + if [[ $LOCAL_ARCH == "arm64" ]]; then + PREMAKE_MAKE_ARCH=ARM + else + PREMAKE_MAKE_ARCH=x86 + fi + make -f Bootstrap.mak osx PLATFORM=$PREMAKE_MAKE_ARCH + cp bin/release/* ../bin + popd + popd +fi + +export PREMAKE=$PWD/dependencies/bin/premake5 + +for var in "$@"; do + if [[ $var = "clean" ]]; then + echo 'Cleaning...' + rm -fR out + fi +done + +mkdir -p out +mkdir -p out_with_renames + +pushd ../../../ +PACKAGES=$PWD +popd +export PREMAKE_PATH="dependencies/export-compile-commands":"$PACKAGES/runtime_unity/native_plugin/platform":"$PACKAGES/runtime/build":"$PREMAKE_PATH" + +$PREMAKE gmake2 --file=../premake5_yoga_v2.lua --out=out --no-yoga-renames +pushd out +make -j$(($(sysctl -n hw.physicalcpu) + 1)) +popd + +nm out/librive_yoga.a --demangle &>yoga_names.txt + +dart gen_header.dart + +# make with renames just to examine the exported symbols... +$PREMAKE gmake2 --file=../premake5_yoga_v2.lua --out=out_with_renames +pushd out_with_renames +make -j$(($(sysctl -n hw.physicalcpu) + 1)) +popd + +nm out_with_renames/librive_yoga.a --demangle &>yoga_renames.txt diff --git a/dependencies/jconfig.h b/dependencies/jconfig.h new file mode 100644 index 00000000..84c05fc1 --- /dev/null +++ b/dependencies/jconfig.h @@ -0,0 +1,13 @@ +#include // Required on Mac -- libjpg expects FILE to be already defined. + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +#undef CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS +#undef NEED_SHORT_EXTERNAL_NAMES +#undef INCOMPLETE_TYPES_BROKEN diff --git a/dependencies/macosx/make_viewer_skia.sh b/dependencies/macosx/make_viewer_skia.sh index f0294366..507781e0 100755 --- a/dependencies/macosx/make_viewer_skia.sh +++ b/dependencies/macosx/make_viewer_skia.sh @@ -17,7 +17,7 @@ else cd skia fi -python tools/git-sync-deps +python3 tools/git-sync-deps CONFIG=debug RENDERER= diff --git a/dependencies/premake5_harfbuzz.lua b/dependencies/premake5_harfbuzz.lua index 1123265c..768cb138 100644 --- a/dependencies/premake5_harfbuzz.lua +++ b/dependencies/premake5_harfbuzz.lua @@ -349,4 +349,10 @@ do targetdir('%{cfg.system}/cache/arm64/bin/%{cfg.buildcfg}') objdir('%{cfg.system}/cache/arm64/obj/%{cfg.buildcfg}') end + + filter('system:macosx or system:ios') + do + defines({ 'HAVE_CORETEXT' }) + files({ harfbuzz .. '/src/hb-coretext.cc' }) + end end diff --git a/dependencies/premake5_harfbuzz_v2.lua b/dependencies/premake5_harfbuzz_v2.lua index 183c378d..c26c1d28 100644 --- a/dependencies/premake5_harfbuzz_v2.lua +++ b/dependencies/premake5_harfbuzz_v2.lua @@ -1,7 +1,7 @@ dofile('rive_build_config.lua') local dependency = require('dependency') -harfbuzz = dependency.github('rive-app/harfbuzz', 'rive_8.3.0') +harfbuzz = dependency.github('rive-app/harfbuzz', 'rive_8.4.0') newoption({ trigger = 'no-harfbuzz-renames', @@ -225,6 +225,7 @@ do warnings('Off') defines({ + 'HB_ONLY_ONE_SHAPER', -- added this for Geotech Mac multi-module issue: https://github.com/rive-app/rive-cpp/issues/369 'HAVE_OT', 'HB_NO_FALLBACK_SHAPE', 'HB_NO_WIN1256', @@ -233,6 +234,8 @@ do 'HB_NO_COLOR', 'HB_NO_BITMAP', 'HB_NO_BUFFER_SERIALIZE', + 'HB_NO_BUFFER_VERIFY', + 'HB_NO_BUFFER_MESSAGE', 'HB_NO_SETLOCALE', 'HB_NO_STYLE', 'HB_NO_VERTICAL', @@ -240,6 +243,9 @@ do 'HB_NO_LAYOUT_RARELY_USED', 'HB_NO_LAYOUT_UNUSED', 'HB_NO_OT_FONT_GLYPH_NAMES', + 'HB_NO_PAINT', + 'HB_NO_MMAP', + 'HB_NO_META', }) filter('toolset:not msc') @@ -268,4 +274,10 @@ do includedirs({ './' }) forceincludes({ 'rive_harfbuzz_renames.h' }) end + + filter('system:macosx or system:ios') + do + defines({ 'HAVE_CORETEXT' }) + files({ harfbuzz .. '/src/hb-coretext.cc' }) + end end diff --git a/dependencies/premake5_libjpeg_v2.lua b/dependencies/premake5_libjpeg_v2.lua new file mode 100644 index 00000000..3ed2ecc0 --- /dev/null +++ b/dependencies/premake5_libjpeg_v2.lua @@ -0,0 +1,72 @@ +dofile('rive_build_config.lua') + +local dependency = require('dependency') +libjpeg = dependency.github('rive-app/libjpeg', 'v9f') + +newoption({ + trigger = 'no-libjpeg-renames', + description = 'don\'t rename libjpeg symbols', +}) + +project('libjpeg') +do + kind('StaticLib') + optimize('Speed') -- Always optimize image encoding/decoding, even in debug builds. + + includedirs({ libjpeg }) + + files({ + libjpeg .. '/jaricom.c', + libjpeg .. '/jcapimin.c', + libjpeg .. '/jcapistd.c', + libjpeg .. '/jcarith.c', + libjpeg .. '/jccoefct.c', + libjpeg .. '/jccolor.c', + libjpeg .. '/jcdctmgr.c', + libjpeg .. '/jchuff.c', + libjpeg .. '/jcinit.c', + libjpeg .. '/jcmainct.c', + libjpeg .. '/jcmarker.c', + libjpeg .. '/jcmaster.c', + libjpeg .. '/jcomapi.c', + libjpeg .. '/jcparam.c', + libjpeg .. '/jcprepct.c', + libjpeg .. '/jcsample.c', + libjpeg .. '/jctrans.c', + libjpeg .. '/jdapimin.c', + libjpeg .. '/jdapistd.c', + libjpeg .. '/jdarith.c', + libjpeg .. '/jdatadst.c', + libjpeg .. '/jdatasrc.c', + libjpeg .. '/jdcoefct.c', + libjpeg .. '/jdcolor.c', + libjpeg .. '/jddctmgr.c', + libjpeg .. '/jdhuff.c', + libjpeg .. '/jdinput.c', + libjpeg .. '/jdmainct.c', + libjpeg .. '/jdmarker.c', + libjpeg .. '/jdmaster.c', + libjpeg .. '/jdmerge.c', + libjpeg .. '/jdpostct.c', + libjpeg .. '/jdsample.c', + libjpeg .. '/jdtrans.c', + libjpeg .. '/jerror.c', + libjpeg .. '/jfdctflt.c', + libjpeg .. '/jfdctfst.c', + libjpeg .. '/jfdctint.c', + libjpeg .. '/jidctflt.c', + libjpeg .. '/jidctfst.c', + libjpeg .. '/jidctint.c', + libjpeg .. '/jquant1.c', + libjpeg .. '/jquant2.c', + libjpeg .. '/jutils.c', + libjpeg .. '/jmemmgr.c', + libjpeg .. '/jmemansi.c', + }) + + filter({ 'options:not no-libjpeg-renames' }) + do + includedirs({ './' }) + forceincludes({ 'rive_libjpeg_renames.h' }) + end +end diff --git a/dependencies/premake5_libpng_v2.lua b/dependencies/premake5_libpng_v2.lua index ac653b79..75e0c8c7 100644 --- a/dependencies/premake5_libpng_v2.lua +++ b/dependencies/premake5_libpng_v2.lua @@ -12,6 +12,7 @@ do kind('StaticLib') os.copyfile(libpng .. '/scripts/pnglibconf.h.prebuilt', libpng .. '/pnglibconf.h') includedirs({ libpng, zlib }) + optimize('Speed') -- Always optimize image encoding/decoding, even in debug builds. files({ libpng .. '/png.c', libpng .. '/pngerror.c', @@ -44,6 +45,7 @@ do kind('StaticLib') defines({ 'ZLIB_IMPLEMENTATION' }) includedirs({ zlib }) + optimize('Speed') -- Always optimize image encoding/decoding, even in debug builds. files({ zlib .. '/adler32.c', zlib .. '/compress.c', @@ -68,6 +70,7 @@ do buildoptions({ '-Wno-unknown-warning-option', '-Wno-deprecated-non-prototype', + '-Wno-shorten-64-to-32', }) end diff --git a/dependencies/premake5_libwebp_v2.lua b/dependencies/premake5_libwebp_v2.lua new file mode 100644 index 00000000..00eb5f18 --- /dev/null +++ b/dependencies/premake5_libwebp_v2.lua @@ -0,0 +1,166 @@ +dofile('rive_build_config.lua') + +local dependency = require('dependency') +libwebp = dependency.github('webmproject/libwebp', 'v1.4.0') + +project('libwebp') +do + kind('StaticLib') + optimize('Speed') -- Always optimize image encoding/decoding, even in debug builds. + + includedirs({ libwebp }) + + -- Leaving some notes here for future perf improvements. Define these when + -- we can determine we're on a compatible platform/perf gain is worth it. + -- + -- Some extra details about each of these: + -- https://github.com/webmproject/libwebp/blob/main/cmake/config.h.in + defines({ + -- 'WEBP_USE_NEON=1', + -- 'WEBP_HAVE_NEON_RTCD=1', -- runtime detection of NEON extensions + -- 'WEBP_HAVE_SSE41=1', + -- 'WEBP_USE_THREAD=1' + }) + + files({ + -- common dsp + libwebp .. '/src/dsp/alpha_processing.c', + libwebp .. '/src/dsp/cpu.c', + libwebp .. '/src/dsp/dec.c', + libwebp .. '/src/dsp/dec_clip_tables.c', + libwebp .. '/src/dsp/filters.c', + libwebp .. '/src/dsp/lossless.c', + libwebp .. '/src/dsp/rescaler.c', + libwebp .. '/src/dsp/upsampling.c', + libwebp .. '/src/dsp/yuv.c', + + -- encoder dsp + libwebp .. '/src/dsp/cost.c', + libwebp .. '/src/dsp/enc.c', + libwebp .. '/src/dsp/lossless_enc.c', + libwebp .. '/src/dsp/ssim.c', + + -- decoder + libwebp .. '/src/dec/alpha_dec.c', + libwebp .. '/src/dec/buffer_dec.c', + libwebp .. '/src/dec/frame_dec.c', + libwebp .. '/src/dec/idec_dec.c', + libwebp .. '/src/dec/io_dec.c', + libwebp .. '/src/dec/quant_dec.c', + libwebp .. '/src/dec/tree_dec.c', + libwebp .. '/src/dec/vp8_dec.c', + libwebp .. '/src/dec/vp8l_dec.c', + libwebp .. '/src/dec/webp_dec.c', + + -- libwebpdspdecode_sse41_la_SOURCES = + libwebp .. '/src/dsp/alpha_processing_sse41.c', + libwebp .. '/src/dsp/dec_sse41.c', + libwebp .. '/src/dsp/lossless_sse41.c', + libwebp .. '/src/dsp/upsampling_sse41.c', + libwebp .. '/src/dsp/yuv_sse41.c', + + -- libwebpdspdecode_sse2_la_SOURCES = + libwebp .. '/src/dsp/alpha_processing_sse2.c', + libwebp .. '/src/dsp/common_sse2.h', + libwebp .. '/src/dsp/dec_sse2.c', + libwebp .. '/src/dsp/filters_sse2.c', + libwebp .. '/src/dsp/lossless_sse2.c', + libwebp .. '/src/dsp/rescaler_sse2.c', + libwebp .. '/src/dsp/upsampling_sse2.c', + libwebp .. '/src/dsp/yuv_sse2.c', + + -- neon sources + -- TODO: define WEBP_HAVE_NEON when we're on a platform that supports it. + libwebp .. '/src/dsp/alpha_processing_neon.c', + libwebp .. '/src/dsp/dec_neon.c', + libwebp .. '/src/dsp/filters_neon.c', + libwebp .. '/src/dsp/lossless_neon.c', + libwebp .. '/src/dsp/neon.h', + libwebp .. '/src/dsp/rescaler_neon.c', + libwebp .. '/src/dsp/upsampling_neon.c', + libwebp .. '/src/dsp/yuv_neon.c', + + -- libwebpdspdecode_msa_la_SOURCES = + libwebp .. '/src/dsp/dec_msa.c', + libwebp .. '/src/dsp/filters_msa.c', + libwebp .. '/src/dsp/lossless_msa.c', + libwebp .. '/src/dsp/msa_macro.h', + libwebp .. '/src/dsp/rescaler_msa.c', + libwebp .. '/src/dsp/upsampling_msa.c', + + -- libwebpdspdecode_mips32_la_SOURCES = + libwebp .. '/src/dsp/dec_mips32.c', + libwebp .. '/src/dsp/mips_macro.h', + libwebp .. '/src/dsp/rescaler_mips32.c', + libwebp .. '/src/dsp/yuv_mips32.c', + + -- libwebpdspdecode_mips_dsp_r2_la_SOURCES = + libwebp .. '/src/dsp/alpha_processing_mips_dsp_r2.c', + libwebp .. '/src/dsp/dec_mips_dsp_r2.c', + libwebp .. '/src/dsp/filters_mips_dsp_r2.c', + libwebp .. '/src/dsp/lossless_mips_dsp_r2.c', + libwebp .. '/src/dsp/mips_macro.h', + libwebp .. '/src/dsp/rescaler_mips_dsp_r2.c', + libwebp .. '/src/dsp/upsampling_mips_dsp_r2.c', + libwebp .. '/src/dsp/yuv_mips_dsp_r2.c', + + -- libwebpdsp_sse2_la_SOURCES = + libwebp .. '/src/dsp/cost_sse2.c', + libwebp .. '/src/dsp/enc_sse2.c', + libwebp .. '/src/dsp/lossless_enc_sse2.c', + libwebp .. '/src/dsp/ssim_sse2.c', + + -- libwebpdsp_sse41_la_SOURCES = + libwebp .. '/src/dsp/enc_sse41.c', + libwebp .. '/src/dsp/lossless_enc_sse41.c', + + -- libwebpdsp_neon_la_SOURCES = + libwebp .. '/src/dsp/cost_neon.c', + libwebp .. '/src/dsp/enc_neon.c', + libwebp .. '/src/dsp/lossless_enc_neon.c', + + -- libwebpdsp_msa_la_SOURCES = + libwebp .. '/src/dsp/enc_msa.c', + libwebp .. '/src/dsp/lossless_enc_msa.c', + + -- libwebpdsp_mips32_la_SOURCES = + libwebp .. '/src/dsp/cost_mips32.c', + libwebp .. '/src/dsp/enc_mips32.c', + libwebp .. '/src/dsp/lossless_enc_mips32.c', + + -- libwebpdsp_mips_dsp_r2_la_SOURCES = + libwebp .. '/src/dsp/cost_mips_dsp_r2.c', + libwebp .. '/src/dsp/enc_mips_dsp_r2.c', + libwebp .. '/src/dsp/lossless_enc_mips_dsp_r2.c', + + -- COMMON_SOURCES = + libwebp .. '/src/utils/bit_reader_utils.c', + libwebp .. '/src/utils/bit_reader_utils.h', + libwebp .. '/src/utils/color_cache_utils.c', + libwebp .. '/src/utils/filters_utils.c', + libwebp .. '/src/utils/huffman_utils.c', + libwebp .. '/src/utils/palette.c', + libwebp .. '/src/utils/quant_levels_dec_utils.c', + libwebp .. '/src/utils/rescaler_utils.c', + libwebp .. '/src/utils/random_utils.c', + libwebp .. '/src/utils/thread_utils.c', + libwebp .. '/src/utils/utils.c', + + -- ENC_SOURCES = + libwebp .. '/src/utils/bit_writer_utils.c', + libwebp .. '/src/utils/huffman_encode_utils.c', + libwebp .. '/src/utils/quant_levels_utils.c', + + -- libwebpdemux_la_SOURCES = + libwebp .. '/src/demux/anim_decode.c', + libwebp .. '/src/demux/demux.c', + }) + + filter({ 'system:windows', 'toolset:clang' }) + do + -- https://github.com/webmproject/libwebp/blob/233e86b91f4e0af7833d50013e3b978f825f73f5/src/dsp/cpu.h#L57 + -- webp automaticall enables these for windows so we need to compile + -- with the correct settings or we get an error. + buildoptions({ '-mssse3', '-msse4.1' }) + end +end diff --git a/dependencies/premake5_miniaudio.lua b/dependencies/premake5_miniaudio.lua index a83cc41a..6779bba6 100644 --- a/dependencies/premake5_miniaudio.lua +++ b/dependencies/premake5_miniaudio.lua @@ -1,2 +1,2 @@ local dependency = require('dependency') -miniaudio = dependency.github('rive-app/miniaudio', 'rive_changes') +miniaudio = dependency.github('rive-app/miniaudio', 'rive_changes_4') diff --git a/dependencies/premake5_miniaudio_v2.lua b/dependencies/premake5_miniaudio_v2.lua index b193cb87..c2098b4f 100644 --- a/dependencies/premake5_miniaudio_v2.lua +++ b/dependencies/premake5_miniaudio_v2.lua @@ -1,3 +1,3 @@ dofile('rive_build_config.lua') local dependency = require('dependency') -miniaudio = dependency.github('rive-app/miniaudio', 'rive_changes') +miniaudio = dependency.github('rive-app/miniaudio', 'rive_changes_4') diff --git a/dependencies/premake5_sheenbidi_v2.lua b/dependencies/premake5_sheenbidi_v2.lua index f7410050..147f983d 100644 --- a/dependencies/premake5_sheenbidi_v2.lua +++ b/dependencies/premake5_sheenbidi_v2.lua @@ -2,6 +2,7 @@ dofile('rive_build_config.lua') local dependency = require('dependency') sheenbidi = dependency.github('Tehreer/SheenBidi', 'v2.6') +Headers = sheenbidi .. '/Headers' project('rive_sheenbidi') do @@ -9,7 +10,15 @@ do language('C') warnings('Off') - includedirs({ sheenbidi .. '/Headers' }) + includedirs({ Headers }) + + filter('action:xcode4') + do + -- xcode doesnt like angle brackets except for -isystem + -- should use externalincludedirs but GitHub runners dont have latest premake5 binaries + buildoptions({ '-isystem' .. Headers }) + end + filter({}) buildoptions({ '-Wall', '-ansi', '-pedantic' }) @@ -49,4 +58,12 @@ do defines({ 'SB_CONFIG_UNITY' }) optimize('Size') end + + filter({ 'system:linux' }) + do + buildoptions({ + '-Wno-unused-function', + '-Wno-unused-variable', + }) + end end diff --git a/dependencies/premake5_yoga.lua b/dependencies/premake5_yoga.lua new file mode 100644 index 00000000..bf83e1ba --- /dev/null +++ b/dependencies/premake5_yoga.lua @@ -0,0 +1,138 @@ +local dependency = require('dependency') +yoga = dependency.github('rive-app/yoga', 'rive_changes_v2_0_1') + +workspace('rive') +configurations({ 'debug', 'release' }) + +project('rive_yoga') +do + kind('StaticLib') + language('C++') + cppdialect('C++11') + targetdir('%{cfg.system}/cache/bin/%{cfg.buildcfg}/') + objdir('%{cfg.system}/cache/obj/%{cfg.buildcfg}/') + warnings('Off') + + defines({ 'YOGA_EXPORT=' }) + + includedirs({ yoga }) + + files({ + yoga .. '/yoga/Utils.cpp', + yoga .. '/yoga/YGConfig.cpp', + yoga .. '/yoga/YGLayout.cpp', + yoga .. '/yoga/YGEnums.cpp', + yoga .. '/yoga/YGNodePrint.cpp', + yoga .. '/yoga/YGNode.cpp', + yoga .. '/yoga/YGValue.cpp', + yoga .. '/yoga/YGStyle.cpp', + yoga .. '/yoga/Yoga.cpp', + yoga .. '/yoga/event/event.cpp', + yoga .. '/yoga/log.cpp', + }) + + buildoptions({ '-Wall', '-pedantic' }) + + linkoptions({ '-r' }) + + filter('system:emscripten') + do + buildoptions({ '-pthread' }) + end + + filter('configurations:debug') + do + defines({ 'DEBUG' }) + symbols('On') + end + + filter('toolset:clang') + do + flags({ 'FatalWarnings' }) + buildoptions({ + '-Werror=format', + '-Wimplicit-int-conversion', + '-Werror=vla', + }) + end + + filter('configurations:release') + do + buildoptions({ '-Oz' }) + defines({ 'RELEASE', 'NDEBUG' }) + optimize('On') + end + + filter({ 'system:macosx', 'options:variant=runtime' }) + do + buildoptions({ + '-Wimplicit-float-conversion -fembed-bitcode -arch arm64 -arch x86_64 -isysroot' + .. (os.getenv('MACOS_SYSROOT') or ''), + }) + end + + filter({ 'system:macosx', 'configurations:release' }) + do + buildoptions({ '-flto=full' }) + end + + filter({ 'system:ios' }) + do + buildoptions({ '-flto=full' }) + end + + filter('system:windows') + do + architecture('x64') + defines({ '_USE_MATH_DEFINES' }) + end + + filter({ 'system:ios', 'options:variant=system' }) + do + buildoptions({ + '-mios-version-min=13.0 -fembed-bitcode -arch arm64 -isysroot ' + .. (os.getenv('IOS_SYSROOT') or ''), + }) + end + + filter({ 'system:ios', 'options:variant=emulator' }) + do + buildoptions({ + '--target=arm64-apple-ios13.0.0-simulator', + '-mios-version-min=13.0 -arch arm64 -arch x86_64 -isysroot ' + .. (os.getenv('IOS_SYSROOT') or ''), + }) + targetdir('%{cfg.system}_sim/cache/bin/%{cfg.buildcfg}') + objdir('%{cfg.system}_sim/cache/obj/%{cfg.buildcfg}') + end + + filter({ 'system:android', 'configurations:release' }) + do + buildoptions({ '-flto=full' }) + end + + -- Is there a way to pass 'arch' as a variable here? + filter({ 'system:android', 'options:arch=x86' }) + do + targetdir('%{cfg.system}/cache/x86/bin/%{cfg.buildcfg}') + objdir('%{cfg.system}/cache/x86/obj/%{cfg.buildcfg}') + end + + filter({ 'system:android', 'options:arch=x64' }) + do + targetdir('%{cfg.system}/cache/x64/bin/%{cfg.buildcfg}') + objdir('%{cfg.system}/cache/x64/obj/%{cfg.buildcfg}') + end + + filter({ 'system:android', 'options:arch=arm' }) + do + targetdir('%{cfg.system}/cache/arm/bin/%{cfg.buildcfg}') + objdir('%{cfg.system}/cache/arm/obj/%{cfg.buildcfg}') + end + + filter({ 'system:android', 'options:arch=arm64' }) + do + targetdir('%{cfg.system}/cache/arm64/bin/%{cfg.buildcfg}') + objdir('%{cfg.system}/cache/arm64/obj/%{cfg.buildcfg}') + end +end diff --git a/dependencies/premake5_yoga_v2.lua b/dependencies/premake5_yoga_v2.lua new file mode 100644 index 00000000..68de64ae --- /dev/null +++ b/dependencies/premake5_yoga_v2.lua @@ -0,0 +1,47 @@ +dofile('rive_build_config.lua') + +local dependency = require('dependency') +yoga = dependency.github('rive-app/yoga', 'rive_changes_v2_0_1') + +newoption({ + trigger = 'no-yoga-renames', + description = 'don\'t rename yoga symbols', +}) + +project('rive_yoga') +do + kind('StaticLib') + warnings('Off') + + defines({ 'YOGA_EXPORT=' }) + + includedirs({ yoga }) + + filter('action:xcode4') + do + -- xcode doesnt like angle brackets except for -isystem + -- should use externalincludedirs but GitHub runners dont have latest premake5 binaries + buildoptions({ '-isystem' .. yoga }) + end + filter({}) + + files({ + yoga .. '/yoga/Utils.cpp', + yoga .. '/yoga/YGConfig.cpp', + yoga .. '/yoga/YGLayout.cpp', + yoga .. '/yoga/YGEnums.cpp', + yoga .. '/yoga/YGNodePrint.cpp', + yoga .. '/yoga/YGNode.cpp', + yoga .. '/yoga/YGValue.cpp', + yoga .. '/yoga/YGStyle.cpp', + yoga .. '/yoga/Yoga.cpp', + yoga .. '/yoga/event/event.cpp', + yoga .. '/yoga/log.cpp', + }) + + filter({ 'options:not no-yoga-renames' }) + do + includedirs({ './' }) + forceincludes({ 'rive_yoga_renames.h' }) + end +end diff --git a/dependencies/rive_harfbuzz_renames.h b/dependencies/rive_harfbuzz_renames.h index 80b66c8b..bb8ea909 100644 --- a/dependencies/rive_harfbuzz_renames.h +++ b/dependencies/rive_harfbuzz_renames.h @@ -76,6 +76,8 @@ #define hb_face_t rive_hb_face_t #define hb_table_lazy_loader_t rive_hb_table_lazy_loader_t #define hb_blob_t rive_hb_blob_t +#define glyf_accelerator_t rive_glyf_accelerator_t +#define OT rive_OT #define hb_nonnull_ptr_t rive_hb_nonnull_ptr_t #define hb_atomic_short_t rive_hb_atomic_short_t #define hb_segment_properties_t rive_hb_segment_properties_t @@ -312,9 +314,19 @@ #define hb_syllabic_clear_var rive_hb_syllabic_clear_var #define hb_syllabic_insert_dotted_circles rive_hb_syllabic_insert_dotted_circles #define hb_options rive_hb_options +#define hb_options_initv rive_hb_options_initv +#define minus_1 rive_minus_1 +#define hb_aat_apply_context_t rive_hb_aat_apply_context_t +#define _hb_unicode_is_emoji_Extended_Pictographic rive__hb_unicode_is_emoji_Extended_Pictographic +#define endchar_str rive_endchar_str +#define _hb_ot_name_language_for_mac_code rive__hb_ot_name_language_for_mac_code +#define _hb_ot_name_language_for_ms_code rive__hb_ot_name_language_for_ms_code #define hb_indic_would_substitute_feature_t rive_hb_indic_would_substitute_feature_t #define hb_ot_layout_glyph_props_flags_t rive_hb_ot_layout_glyph_props_flags_t #define hb_use_u16 rive_hb_use_u16 +#define lookup_expert_subset_charset_for_glyph rive_lookup_expert_subset_charset_for_glyph +#define lookup_expert_charset_for_glyph rive_lookup_expert_charset_for_glyph +#define _hb_options_init rive__hb_options_init #define hb_use_get_category rive_hb_use_get_category #define hb_use_b4 rive_hb_use_b4 #define hb_use_u8 rive_hb_use_u8 diff --git a/dependencies/rive_libjpeg_renames.h b/dependencies/rive_libjpeg_renames.h new file mode 100644 index 00000000..9e1ad74f --- /dev/null +++ b/dependencies/rive_libjpeg_renames.h @@ -0,0 +1,204 @@ +// clang-format off +// jpeg_* +#define jpeg_aritab rive_jpeg_aritab +#define jpeg_CreateCompress rive_jpeg_CreateCompress +#define jpeg_abort rive_jpeg_abort +#define jpeg_abort_compress rive_jpeg_abort_compress +#define jpeg_destroy rive_jpeg_destroy +#define jpeg_destroy_compress rive_jpeg_destroy_compress +#define jpeg_finish_compress rive_jpeg_finish_compress +#define jpeg_natural_order rive_jpeg_natural_order +#define jpeg_suppress_tables rive_jpeg_suppress_tables +#define jpeg_write_m_byte rive_jpeg_write_m_byte +#define jpeg_write_m_header rive_jpeg_write_m_header +#define jpeg_write_marker rive_jpeg_write_marker +#define jpeg_write_tables rive_jpeg_write_tables +#define jpeg_start_compress rive_jpeg_start_compress +#define jpeg_write_raw_data rive_jpeg_write_raw_data +#define jpeg_write_scanlines rive_jpeg_write_scanlines +#define jpeg_fdct_10x10 rive_jpeg_fdct_10x10 +#define jpeg_fdct_10x5 rive_jpeg_fdct_10x5 +#define jpeg_fdct_11x11 rive_jpeg_fdct_11x11 +#define jpeg_fdct_12x12 rive_jpeg_fdct_12x12 +#define jpeg_fdct_12x6 rive_jpeg_fdct_12x6 +#define jpeg_fdct_13x13 rive_jpeg_fdct_13x13 +#define jpeg_fdct_14x14 rive_jpeg_fdct_14x14 +#define jpeg_fdct_14x7 rive_jpeg_fdct_14x7 +#define jpeg_fdct_15x15 rive_jpeg_fdct_15x15 +#define jpeg_fdct_16x16 rive_jpeg_fdct_16x16 +#define jpeg_fdct_16x8 rive_jpeg_fdct_16x8 +#define jpeg_fdct_1x1 rive_jpeg_fdct_1x1 +#define jpeg_fdct_1x2 rive_jpeg_fdct_1x2 +#define jpeg_fdct_2x1 rive_jpeg_fdct_2x1 +#define jpeg_fdct_2x2 rive_jpeg_fdct_2x2 +#define jpeg_fdct_2x4 rive_jpeg_fdct_2x4 +#define jpeg_fdct_3x3 rive_jpeg_fdct_3x3 +#define jpeg_fdct_3x6 rive_jpeg_fdct_3x6 +#define jpeg_fdct_4x2 rive_jpeg_fdct_4x2 +#define jpeg_fdct_4x4 rive_jpeg_fdct_4x4 +#define jpeg_fdct_4x8 rive_jpeg_fdct_4x8 +#define jpeg_fdct_5x10 rive_jpeg_fdct_5x10 +#define jpeg_fdct_5x5 rive_jpeg_fdct_5x5 +#define jpeg_fdct_6x12 rive_jpeg_fdct_6x12 +#define jpeg_fdct_6x3 rive_jpeg_fdct_6x3 +#define jpeg_fdct_6x6 rive_jpeg_fdct_6x6 +#define jpeg_fdct_7x14 rive_jpeg_fdct_7x14 +#define jpeg_fdct_7x7 rive_jpeg_fdct_7x7 +#define jpeg_fdct_8x16 rive_jpeg_fdct_8x16 +#define jpeg_fdct_8x4 rive_jpeg_fdct_8x4 +#define jpeg_fdct_9x9 rive_jpeg_fdct_9x9 +#define jpeg_fdct_float rive_jpeg_fdct_float +#define jpeg_fdct_ifast rive_jpeg_fdct_ifast +#define jpeg_fdct_islow rive_jpeg_fdct_islow +#define jpeg_alloc_huff_table rive_jpeg_alloc_huff_table +#define jpeg_gen_optimal_table rive_jpeg_gen_optimal_table +#define jpeg_make_c_derived_tbl rive_jpeg_make_c_derived_tbl +#define jpeg_std_huff_table rive_jpeg_std_huff_table +#define jpeg_calc_jpeg_dimensions rive_jpeg_calc_jpeg_dimensions +#define jpeg_natural_order2 rive_jpeg_natural_order2 +#define jpeg_natural_order3 rive_jpeg_natural_order3 +#define jpeg_natural_order4 rive_jpeg_natural_order4 +#define jpeg_natural_order5 rive_jpeg_natural_order5 +#define jpeg_natural_order6 rive_jpeg_natural_order6 +#define jpeg_natural_order7 rive_jpeg_natural_order7 +#define jpeg_alloc_quant_table rive_jpeg_alloc_quant_table +#define jpeg_add_quant_table rive_jpeg_add_quant_table +#define jpeg_default_colorspace rive_jpeg_default_colorspace +#define jpeg_default_qtables rive_jpeg_default_qtables +#define jpeg_quality_scaling rive_jpeg_quality_scaling +#define jpeg_set_colorspace rive_jpeg_set_colorspace +#define jpeg_set_defaults rive_jpeg_set_defaults +#define jpeg_set_linear_quality rive_jpeg_set_linear_quality +#define jpeg_set_quality rive_jpeg_set_quality +#define jpeg_simple_progression rive_jpeg_simple_progression +#define jpeg_copy_critical_parameters rive_jpeg_copy_critical_parameters +#define jpeg_write_coefficients rive_jpeg_write_coefficients +#define jpeg_CreateDecompress rive_jpeg_CreateDecompress +#define jpeg_abort_decompress rive_jpeg_abort_decompress +#define jpeg_consume_input rive_jpeg_consume_input +#define jpeg_destroy_decompress rive_jpeg_destroy_decompress +#define jpeg_finish_decompress rive_jpeg_finish_decompress +#define jpeg_has_multiple_scans rive_jpeg_has_multiple_scans +#define jpeg_input_complete rive_jpeg_input_complete +#define jpeg_read_header rive_jpeg_read_header +#define jpeg_finish_output rive_jpeg_finish_output +#define jpeg_read_raw_data rive_jpeg_read_raw_data +#define jpeg_read_scanlines rive_jpeg_read_scanlines +#define jpeg_start_decompress rive_jpeg_start_decompress +#define jpeg_start_output rive_jpeg_start_output +#define jpeg_mem_dest rive_jpeg_mem_dest +#define jpeg_stdio_dest rive_jpeg_stdio_dest +#define jpeg_mem_src rive_jpeg_mem_src +#define jpeg_resync_to_restart rive_jpeg_resync_to_restart +#define jpeg_stdio_src rive_jpeg_stdio_src +#define jpeg_idct_10x10 rive_jpeg_idct_10x10 +#define jpeg_idct_10x5 rive_jpeg_idct_10x5 +#define jpeg_idct_11x11 rive_jpeg_idct_11x11 +#define jpeg_idct_12x12 rive_jpeg_idct_12x12 +#define jpeg_idct_12x6 rive_jpeg_idct_12x6 +#define jpeg_idct_13x13 rive_jpeg_idct_13x13 +#define jpeg_idct_14x14 rive_jpeg_idct_14x14 +#define jpeg_idct_14x7 rive_jpeg_idct_14x7 +#define jpeg_idct_15x15 rive_jpeg_idct_15x15 +#define jpeg_idct_16x16 rive_jpeg_idct_16x16 +#define jpeg_idct_16x8 rive_jpeg_idct_16x8 +#define jpeg_idct_1x1 rive_jpeg_idct_1x1 +#define jpeg_idct_1x2 rive_jpeg_idct_1x2 +#define jpeg_idct_2x1 rive_jpeg_idct_2x1 +#define jpeg_idct_2x2 rive_jpeg_idct_2x2 +#define jpeg_idct_2x4 rive_jpeg_idct_2x4 +#define jpeg_idct_3x3 rive_jpeg_idct_3x3 +#define jpeg_idct_3x6 rive_jpeg_idct_3x6 +#define jpeg_idct_4x2 rive_jpeg_idct_4x2 +#define jpeg_idct_4x4 rive_jpeg_idct_4x4 +#define jpeg_idct_4x8 rive_jpeg_idct_4x8 +#define jpeg_idct_5x10 rive_jpeg_idct_5x10 +#define jpeg_idct_5x5 rive_jpeg_idct_5x5 +#define jpeg_idct_6x12 rive_jpeg_idct_6x12 +#define jpeg_idct_6x3 rive_jpeg_idct_6x3 +#define jpeg_idct_6x6 rive_jpeg_idct_6x6 +#define jpeg_idct_7x14 rive_jpeg_idct_7x14 +#define jpeg_idct_7x7 rive_jpeg_idct_7x7 +#define jpeg_idct_8x16 rive_jpeg_idct_8x16 +#define jpeg_idct_8x4 rive_jpeg_idct_8x4 +#define jpeg_idct_9x9 rive_jpeg_idct_9x9 +#define jpeg_idct_float rive_jpeg_idct_float +#define jpeg_idct_ifast rive_jpeg_idct_ifast +#define jpeg_idct_islow rive_jpeg_idct_islow +#define jpeg_fill_bit_buffer rive_jpeg_fill_bit_buffer +#define jpeg_huff_decode rive_jpeg_huff_decode +#define jpeg_make_d_derived_tbl rive_jpeg_make_d_derived_tbl +#define jpeg_zigzag_order rive_jpeg_zigzag_order +#define jpeg_zigzag_order2 rive_jpeg_zigzag_order2 +#define jpeg_zigzag_order3 rive_jpeg_zigzag_order3 +#define jpeg_zigzag_order4 rive_jpeg_zigzag_order4 +#define jpeg_zigzag_order5 rive_jpeg_zigzag_order5 +#define jpeg_zigzag_order6 rive_jpeg_zigzag_order6 +#define jpeg_zigzag_order7 rive_jpeg_zigzag_order7 +#define jpeg_core_output_dimensions rive_jpeg_core_output_dimensions +#define jpeg_save_markers rive_jpeg_save_markers +#define jpeg_set_marker_processor rive_jpeg_set_marker_processor +#define jpeg_calc_output_dimensions rive_jpeg_calc_output_dimensions +#define jpeg_new_colormap rive_jpeg_new_colormap +#define jpeg_read_coefficients rive_jpeg_read_coefficients +#define jpeg_std_error rive_jpeg_std_error +#define jpeg_std_message_table rive_jpeg_std_message_table +#define jpeg_free_large rive_jpeg_free_large +#define jpeg_free_small rive_jpeg_free_small +#define jpeg_get_large rive_jpeg_get_large +#define jpeg_get_small rive_jpeg_get_small +#define jpeg_mem_available rive_jpeg_mem_available +#define jpeg_mem_init rive_jpeg_mem_init +#define jpeg_mem_term rive_jpeg_mem_term +#define jpeg_open_backing_store rive_jpeg_open_backing_store +// jinit_* +#define jinit_marker_writer rive_jinit_marker_writer +#define jinit_memory_mgr rive_jinit_memory_mgr +#define jinit_compress_master rive_jinit_compress_master +#define jinit_arith_encoder rive_jinit_arith_encoder +#define jinit_c_coef_controller rive_jinit_c_coef_controller +#define jinit_color_converter rive_jinit_color_converter +#define jinit_forward_dct rive_jinit_forward_dct +#define jinit_huff_encoder rive_jinit_huff_encoder +#define jinit_c_main_controller rive_jinit_c_main_controller +#define jinit_c_master_control rive_jinit_c_master_control +#define jinit_c_prep_controller rive_jinit_c_prep_controller +#define jinit_downsampler rive_jinit_downsampler +#define jinit_input_controller rive_jinit_input_controller +#define jinit_marker_reader rive_jinit_marker_reader +#define jinit_master_decompress rive_jinit_master_decompress +#define jinit_arith_decoder rive_jinit_arith_decoder +#define jinit_d_coef_controller rive_jinit_d_coef_controller +#define jinit_color_deconverter rive_jinit_color_deconverter +#define jinit_inverse_dct rive_jinit_inverse_dct +#define jinit_huff_decoder rive_jinit_huff_decoder +#define jinit_d_main_controller rive_jinit_d_main_controller +#define jinit_1pass_quantizer rive_jinit_1pass_quantizer +#define jinit_2pass_quantizer rive_jinit_2pass_quantizer +#define jinit_d_post_controller rive_jinit_d_post_controller +#define jinit_merged_upsampler rive_jinit_merged_upsampler +#define jinit_upsampler rive_jinit_upsampler +// _j extras +#define jtransform_execute_transform rive_jtransform_execute_transform +#define read_scan_script rive_read_scan_script +#define jcopy_markers_execute rive_jcopy_markers_execute +#define jcopy_markers_setup rive_jcopy_markers_setup +#define enable_signal_catcher rive_enable_signal_catcher +#define set_quant_slots rive_set_quant_slots +#define read_stdin rive_read_stdin +#define set_quality_ratings rive_set_quality_ratings +#define write_stdout rive_write_stdout +#define set_sample_factors rive_set_sample_factors +#define jtransform_perfect_transform rive_jtransform_perfect_transform +#define jcopy_sample_rows rive_jcopy_sample_rows +#define jdiv_round_up rive_jdiv_round_up +#define jtransform_request_workspace rive_jtransform_request_workspace +#define jcopy_block_row rive_jcopy_block_row +#define end_progress_monitor rive_end_progress_monitor +#define read_quant_tables rive_read_quant_tables +#define jzero_far rive_jzero_far +#define read_color_map rive_read_color_map +#define jtransform_adjust_parameters rive_jtransform_adjust_parameters +#define jround_up rive_jround_up +#define start_progress_monitor rive_start_progress_monitor +#define jtransform_parse_crop_spec rive_jtransform_parse_crop_spec diff --git a/dependencies/rive_png_renames.h b/dependencies/rive_png_renames.h index 5a15fb01..5122b8c5 100644 --- a/dependencies/rive_png_renames.h +++ b/dependencies/rive_png_renames.h @@ -2,6 +2,8 @@ #define RIVE_PNG_RENAMES_H #define PNGPREFIX_H #define PNG_PREFIX rive_ +#define png_image_write_to_memory rive_png_image_write_to_memory +#define png_check_keyword rive_png_check_keyword #define png_sRGB_table rive_png_sRGB_table #define png_sRGB_base rive_png_sRGB_base #define png_sRGB_delta rive_png_sRGB_delta diff --git a/dependencies/rive_yoga_renames.h b/dependencies/rive_yoga_renames.h new file mode 100644 index 00000000..f10efae0 --- /dev/null +++ b/dependencies/rive_yoga_renames.h @@ -0,0 +1,241 @@ +// clang-format off +// YG* +#define YGFloatMax rive_YGFloatMax +#define YGFloatMin rive_YGFloatMin +#define YGValueEqual rive_YGValueEqual +#define YGValue rive_YGValue +#define YGDoubleEqual rive_YGDoubleEqual +#define YGFloatsEqual rive_YGFloatsEqual +#define YGFloatSanitize rive_YGFloatSanitize +#define YGFloatOptionalMax rive_YGFloatOptionalMax +#define YGFloatOptional rive_YGFloatOptional +#define YGFlexDirectionCross rive_YGFlexDirectionCross +#define YGDirection rive_YGDirection +#define YGResolveFlexDirection rive_YGResolveFlexDirection +#define YGFlexDirectionIsColumn rive_YGFlexDirectionIsColumn +#define YGConfig rive_YGConfig +#define YGNode rive_YGNode +#define YGLogLevel rive_YGLogLevel +#define YGErrata rive_YGErrata +#define YGEnums rive_YGEnums +#define YGLayout rive_YGLayout +#define YGFloatArrayEqual rive_YGFloatArrayEqual +#define YGCachedMeasurement rive_YGCachedMeasurement +#define YGResolveValue rive_YGResolveValue +#define YGFlexDirectionIsRow rive_YGFlexDirectionIsRow +#define YGResolveValueMargin rive_YGResolveValueMargin +#define YGMeasureMode rive_YGMeasureMode +#define YGEdge rive_YGEdge +#define YGStyle rive_YGStyle +#define YGPositionType rive_YGPositionType +#define YGFlexDirection rive_YGFlexDirection +#define YGNodePrint rive_YGNodePrint +#define YGPrintOptions rive_YGPrintOptions +#define YGOverflow rive_YGOverflow +#define YGWrap rive_YGWrap +#define YGAlign rive_YGAlign +#define YGDisplay rive_YGDisplay +#define YGJustify rive_YGJustify +#define YGLayoutNodeInternal rive_YGLayoutNodeInternal +#define YGConfigGetDefault rive_YGConfigGetDefault +#define YGBaseline rive_YGBaseline +#define YGDefaultLog rive_YGDefaultLog +#define YGNodeAlignItem rive_YGNodeAlignItem +#define YGNodeBoundAxis rive_YGNodeBoundAxis +#define YGNodelayoutImpl rive_YGNodelayoutImpl +#define YGJustifyMainAxis rive_YGJustifyMainAxis +#define YGCollectFlexItemsRowValues rive_YGCollectFlexItemsRowValues +#define YGMeasureModeName rive_YGMeasureModeName +#define YGIsBaselineLayout rive_YGIsBaselineLayout +#define YGRoundToPixelGrid rive_YGRoundToPixelGrid +#define YGDoubleIsUndefined rive_YGDoubleIsUndefined +#define YGNodeDimWithMargin rive_YGNodeDimWithMargin +#define YGNodePrintInternal rive_YGNodePrintInternal +#define YGNodeIsStyleDimDefined rive_YGNodeIsStyleDimDefined +#define YGResolveFlexibleLength rive_YGResolveFlexibleLength +#define YGNodeIsLayoutDimDefined rive_YGNodeIsLayoutDimDefined +#define YGConstrainMaxSizeForMode rive_YGConstrainMaxSizeForMode +#define YGNodeAbsoluteLayoutChild rive_YGNodeAbsoluteLayoutChild +#define YGZeroOutLayoutRecursively rive_YGZeroOutLayoutRecursively +#define YGNodePaddingAndBorderForAxis rive_YGNodePaddingAndBorderForAxis +#define YGDistributeFreeSpaceFirstPass rive_YGDistributeFreeSpaceFirstPass +#define YGNodeBoundAxisWithinMinAndMax rive_YGNodeBoundAxisWithinMinAndMax +#define YGNodeComputeFlexBasisForChild rive_YGNodeComputeFlexBasisForChild +#define YGNodeSetChildTrailingPosition rive_YGNodeSetChildTrailingPosition +#define YGDistributeFreeSpaceSecondPass rive_YGDistributeFreeSpaceSecondPass +#define YGNodeCalculateAvailableInnerDim rive_YGNodeCalculateAvailableInnerDim +#define YGDimension rive_YGDimension +#define YGNodeComputeFlexBasisForChildren rive_YGNodeComputeFlexBasisForChildren +#define YGCalculateCollectFlexItemsRowValues rive_YGCalculateCollectFlexItemsRowValues +#define YGNodeFixedSizeSetMeasuredDimensions rive_YGNodeFixedSizeSetMeasuredDimensions +#define YGNodeEmptyContainerSetMeasuredDimensions rive_YGNodeEmptyContainerSetMeasuredDimensions +#define YGNodeWithMeasureFuncSetMeasuredDimensions rive_YGNodeWithMeasureFuncSetMeasuredDimensions +#define YGMeasureModeOldSizeIsUnspecifiedAndStillFits rive_YGMeasureModeOldSizeIsUnspecifiedAndStillFits +#define YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize rive_YGMeasureModeSizeIsExactAndMatchesOldMeasuredSize +#define YGMeasureModeNewMeasureSizeIsStricterAndStillValid rive_YGMeasureModeNewMeasureSizeIsStricterAndStillValid +#define YGSpacer rive_YGSpacer +#define YGGutter rive_YGGutter +// _YG* +#define YGNodeClone rive_YGNodeClone +#define YGAlignToString rive_YGAlignToString +#define YGDimensionToString rive_YGDimensionToString +#define YGDirectionToString rive_YGDirectionToString +#define YGDisplayToString rive_YGDisplayToString +#define YGEdgeToString rive_YGEdgeToString +#define YGErrataToString rive_YGErrataToString +#define YGExperimentalFeatureToString rive_YGExperimentalFeatureToString +#define YGFlexDirectionToString rive_YGFlexDirectionToString +#define YGGutterToString rive_YGGutterToString +#define YGJustifyToString rive_YGJustifyToString +#define YGLogLevelToString rive_YGLogLevelToString +#define YGMeasureModeToString rive_YGMeasureModeToString +#define YGNodeTypeToString rive_YGNodeTypeToString +#define YGOverflowToString rive_YGOverflowToString +#define YGPositionTypeToString rive_YGPositionTypeToString +#define YGPrintOptionsToString rive_YGPrintOptionsToString +#define YGUnitToString rive_YGUnitToString +#define YGWrapToString rive_YGWrapToString +#define YGAssert rive_YGAssert +#define YGAssertWithConfig rive_YGAssertWithConfig +#define YGAssertWithNode rive_YGAssertWithNode +#define YGValueAuto rive_YGValueAuto +#define YGValueUndefined rive_YGValueUndefined +#define YGValueZero rive_YGValueZero +#define YGNodeGetChild rive_YGNodeGetChild +#define YGConfigCopy rive_YGConfigCopy +#define YGConfigFree rive_YGConfigFree +#define YGConfigGetContext rive_YGConfigGetContext +#define YGConfigGetErrata rive_YGConfigGetErrata +#define YGConfigGetInstanceCount rive_YGConfigGetInstanceCount +#define YGConfigGetPointScaleFactor rive_YGConfigGetPointScaleFactor +#define YGConfigGetUseLegacyStretchBehaviour rive_YGConfigGetUseLegacyStretchBehaviour +#define YGConfigGetUseWebDefaults rive_YGConfigGetUseWebDefaults +#define YGConfigIsExperimentalFeatureEnabled rive_YGConfigIsExperimentalFeatureEnabled +#define YGConfigNew rive_YGConfigNew +#define YGConfigSetCloneNodeFunc rive_YGConfigSetCloneNodeFunc +#define YGConfigSetContext rive_YGConfigSetContext +#define YGConfigSetErrata rive_YGConfigSetErrata +#define YGConfigSetExperimentalFeatureEnabled rive_YGConfigSetExperimentalFeatureEnabled +#define YGConfigSetLogger rive_YGConfigSetLogger +#define YGConfigSetPointScaleFactor rive_YGConfigSetPointScaleFactor +#define YGConfigSetPrintTreeFlag rive_YGConfigSetPrintTreeFlag +#define YGConfigSetUseLegacyStretchBehaviour rive_YGConfigSetUseLegacyStretchBehaviour +#define YGConfigSetUseWebDefaults rive_YGConfigSetUseWebDefaults +#define YGFloatIsUndefined rive_YGFloatIsUndefined +#define YGNodeCalculateLayout rive_YGNodeCalculateLayout +#define YGNodeCalculateLayoutWithContext rive_YGNodeCalculateLayoutWithContext +#define YGNodeCanUseCachedMeasurement rive_YGNodeCanUseCachedMeasurement +#define YGNodeCopyStyle rive_YGNodeCopyStyle +#define YGNodeDeallocate rive_YGNodeDeallocate +#define YGNodeFree rive_YGNodeFree +#define YGNodeFreeRecursive rive_YGNodeFreeRecursive +#define YGNodeFreeRecursiveWithCleanupFunc rive_YGNodeFreeRecursiveWithCleanupFunc +#define YGNodeGetChildCount rive_YGNodeGetChildCount +#define YGNodeGetConfig rive_YGNodeGetConfig +#define YGNodeGetContext rive_YGNodeGetContext +#define YGNodeGetDirtiedFunc rive_YGNodeGetDirtiedFunc +#define YGNodeGetHasNewLayout rive_YGNodeGetHasNewLayout +#define YGNodeGetNodeType rive_YGNodeGetNodeType +#define YGNodeGetOwner rive_YGNodeGetOwner +#define YGNodeGetParent rive_YGNodeGetParent +#define YGNodeHasBaselineFunc rive_YGNodeHasBaselineFunc +#define YGNodeHasMeasureFunc rive_YGNodeHasMeasureFunc +#define YGNodeInsertChild rive_YGNodeInsertChild +#define YGNodeIsDirty rive_YGNodeIsDirty +#define YGNodeIsReferenceBaseline rive_YGNodeIsReferenceBaseline +#define YGNodeLayoutGetBorder rive_YGNodeLayoutGetBorder +#define YGNodeLayoutGetBottom rive_YGNodeLayoutGetBottom +#define YGNodeLayoutGetDirection rive_YGNodeLayoutGetDirection +#define YGNodeLayoutGetHadOverflow rive_YGNodeLayoutGetHadOverflow +#define YGNodeLayoutGetHeight rive_YGNodeLayoutGetHeight +#define YGNodeLayoutGetLeft rive_YGNodeLayoutGetLeft +#define YGNodeLayoutGetMargin rive_YGNodeLayoutGetMargin +#define YGNodeLayoutGetPadding rive_YGNodeLayoutGetPadding +#define YGNodeLayoutGetRight rive_YGNodeLayoutGetRight +#define YGNodeLayoutGetTop rive_YGNodeLayoutGetTop +#define YGNodeLayoutGetWidth rive_YGNodeLayoutGetWidth +#define YGNodeMarkDirty rive_YGNodeMarkDirty +#define YGNodeMarkDirtyAndPropagateToDescendants rive_YGNodeMarkDirtyAndPropagateToDescendants +#define YGNodeNew rive_YGNodeNew +#define YGNodeNewWithConfig rive_YGNodeNewWithConfig +#define YGNodeRemoveAllChildren rive_YGNodeRemoveAllChildren +#define YGNodeRemoveChild rive_YGNodeRemoveChild +#define YGNodeReset rive_YGNodeReset +#define YGNodeSetBaselineFunc rive_YGNodeSetBaselineFunc +#define YGNodeSetChildren rive_YGNodeSetChildren +#define YGNodeSetConfig rive_YGNodeSetConfig +#define YGNodeSetContext rive_YGNodeSetContext +#define YGNodeSetDirtiedFunc rive_YGNodeSetDirtiedFunc +#define YGNodeSetHasNewLayout rive_YGNodeSetHasNewLayout +#define YGNodeSetIsReferenceBaseline rive_YGNodeSetIsReferenceBaseline +#define YGNodeSetMeasureFunc rive_YGNodeSetMeasureFunc +#define YGNodeSetNodeType rive_YGNodeSetNodeType +#define YGNodeSetPrintFunc rive_YGNodeSetPrintFunc +#define YGNodeStyleGetAlignContent rive_YGNodeStyleGetAlignContent +#define YGNodeStyleGetAlignItems rive_YGNodeStyleGetAlignItems +#define YGNodeStyleGetAlignSelf rive_YGNodeStyleGetAlignSelf +#define YGNodeStyleGetAspectRatio rive_YGNodeStyleGetAspectRatio +#define YGNodeStyleGetBorder rive_YGNodeStyleGetBorder +#define YGNodeStyleGetDirection rive_YGNodeStyleGetDirection +#define YGNodeStyleGetDisplay rive_YGNodeStyleGetDisplay +#define YGNodeStyleGetFlex rive_YGNodeStyleGetFlex +#define YGNodeStyleGetFlexBasis rive_YGNodeStyleGetFlexBasis +#define YGNodeStyleGetFlexDirection rive_YGNodeStyleGetFlexDirection +#define YGNodeStyleGetFlexGrow rive_YGNodeStyleGetFlexGrow +#define YGNodeStyleGetFlexShrink rive_YGNodeStyleGetFlexShrink +#define YGNodeStyleGetFlexWrap rive_YGNodeStyleGetFlexWrap +#define YGNodeStyleGetGap rive_YGNodeStyleGetGap +#define YGNodeStyleGetHeight rive_YGNodeStyleGetHeight +#define YGNodeStyleGetJustifyContent rive_YGNodeStyleGetJustifyContent +#define YGNodeStyleGetMargin rive_YGNodeStyleGetMargin +#define YGNodeStyleGetMaxHeight rive_YGNodeStyleGetMaxHeight +#define YGNodeStyleGetMaxWidth rive_YGNodeStyleGetMaxWidth +#define YGNodeStyleGetMinHeight rive_YGNodeStyleGetMinHeight +#define YGNodeStyleGetMinWidth rive_YGNodeStyleGetMinWidth +#define YGNodeStyleGetOverflow rive_YGNodeStyleGetOverflow +#define YGNodeStyleGetPadding rive_YGNodeStyleGetPadding +#define YGNodeStyleGetPosition rive_YGNodeStyleGetPosition +#define YGNodeStyleGetPositionType rive_YGNodeStyleGetPositionType +#define YGNodeStyleGetWidth rive_YGNodeStyleGetWidth +#define YGNodeStyleSetAlignContent rive_YGNodeStyleSetAlignContent +#define YGNodeStyleSetAlignItems rive_YGNodeStyleSetAlignItems +#define YGNodeStyleSetAlignSelf rive_YGNodeStyleSetAlignSelf +#define YGNodeStyleSetAspectRatio rive_YGNodeStyleSetAspectRatio +#define YGNodeStyleSetBorder rive_YGNodeStyleSetBorder +#define YGNodeStyleSetDirection rive_YGNodeStyleSetDirection +#define YGNodeStyleSetDisplay rive_YGNodeStyleSetDisplay +#define YGNodeStyleSetFlex rive_YGNodeStyleSetFlex +#define YGNodeStyleSetFlexBasis rive_YGNodeStyleSetFlexBasis +#define YGNodeStyleSetFlexBasisAuto rive_YGNodeStyleSetFlexBasisAuto +#define YGNodeStyleSetFlexBasisPercent rive_YGNodeStyleSetFlexBasisPercent +#define YGNodeStyleSetFlexDirection rive_YGNodeStyleSetFlexDirection +#define YGNodeStyleSetFlexGrow rive_YGNodeStyleSetFlexGrow +#define YGNodeStyleSetFlexShrink rive_YGNodeStyleSetFlexShrink +#define YGNodeStyleSetFlexWrap rive_YGNodeStyleSetFlexWrap +#define YGNodeStyleSetGap rive_YGNodeStyleSetGap +#define YGNodeStyleSetHeight rive_YGNodeStyleSetHeight +#define YGNodeStyleSetHeightAuto rive_YGNodeStyleSetHeightAuto +#define YGNodeStyleSetHeightPercent rive_YGNodeStyleSetHeightPercent +#define YGNodeStyleSetJustifyContent rive_YGNodeStyleSetJustifyContent +#define YGNodeStyleSetMargin rive_YGNodeStyleSetMargin +#define YGNodeStyleSetMarginAuto rive_YGNodeStyleSetMarginAuto +#define YGNodeStyleSetMarginPercent rive_YGNodeStyleSetMarginPercent +#define YGNodeStyleSetMaxHeight rive_YGNodeStyleSetMaxHeight +#define YGNodeStyleSetMaxHeightPercent rive_YGNodeStyleSetMaxHeightPercent +#define YGNodeStyleSetMaxWidth rive_YGNodeStyleSetMaxWidth +#define YGNodeStyleSetMaxWidthPercent rive_YGNodeStyleSetMaxWidthPercent +#define YGNodeStyleSetMinHeight rive_YGNodeStyleSetMinHeight +#define YGNodeStyleSetMinHeightPercent rive_YGNodeStyleSetMinHeightPercent +#define YGNodeStyleSetMinWidth rive_YGNodeStyleSetMinWidth +#define YGNodeStyleSetMinWidthPercent rive_YGNodeStyleSetMinWidthPercent +#define YGNodeStyleSetOverflow rive_YGNodeStyleSetOverflow +#define YGNodeStyleSetPadding rive_YGNodeStyleSetPadding +#define YGNodeStyleSetPaddingPercent rive_YGNodeStyleSetPaddingPercent +#define YGNodeStyleSetPosition rive_YGNodeStyleSetPosition +#define YGNodeStyleSetPositionPercent rive_YGNodeStyleSetPositionPercent +#define YGNodeStyleSetPositionType rive_YGNodeStyleSetPositionType +#define YGNodeStyleSetWidth rive_YGNodeStyleSetWidth +#define YGNodeStyleSetWidthAuto rive_YGNodeStyleSetWidthAuto +#define YGNodeStyleSetWidthPercent rive_YGNodeStyleSetWidthPercent +#define YGNodeSwapChild rive_YGNodeSwapChild +#define YGRoundValueToPixelGrid rive_YGRoundValueToPixelGrid diff --git a/dev/core_generator/lib/src/definition.dart b/dev/core_generator/lib/src/definition.dart index 6515f77d..bf94a2b7 100644 --- a/dev/core_generator/lib/src/definition.dart +++ b/dev/core_generator/lib/src/definition.dart @@ -30,11 +30,24 @@ class Definition { properties.where((property) => property.getExportType().storesData); Definition? _extensionOf; + Definition? _rawExtensionOf; Key? _key; bool _isAbstract = false; bool _editorOnly = false; bool _forRuntime = true; bool get forRuntime => _forRuntime; + + Definition? getRuntimeExtensionOf(Definition? definition) { + var extensionOf = definition; + if (extensionOf != null) { + if (extensionOf._forRuntime) { + return extensionOf; + } + return getRuntimeExtensionOf(extensionOf._extensionOf); + } + return extensionOf; + } + static Definition? make(String filename) { var definition = definitions[filename]; if (definition != null) { @@ -61,7 +74,8 @@ class Definition { Definition.fromFilename(this._filename, Map data) { dynamic extendsFilename = data['extends']; if (extendsFilename is String) { - _extensionOf = Definition.make(extendsFilename); + _rawExtensionOf = Definition.make(extendsFilename); + _extensionOf = getRuntimeExtensionOf(_rawExtensionOf); } dynamic nameValue = data['name']; if (nameValue is String) { @@ -169,6 +183,11 @@ class Definition { for (final property in properties) { code.writeln('static const uint16_t ${property.name}PropertyKey = ' '${property.key!.intValue};'); + for (final altKey in property.key!.alternates) { + code.writeln( + 'static const uint16_t ${altKey.stringValue}PropertyKey = ' + '${altKey.intValue};'); + } } if (storedProperties.any((prop) => !prop.isEncoded)) { code.writeln('private:'); @@ -220,20 +239,26 @@ class Definition { (property.isSetOverride ? 'override' : '') + '= 0;'); } else { - code.writeln((property.isVirtual ? 'virtual' : 'inline') + + code.writeln(((property.isVirtual || property.isPureVirtual) + ? 'virtual' + : 'inline') + ' ${property.type.cppGetterName} ${property.name}() const ' + (property.isGetOverride ? 'override' : '') + '{ return m_${property.capitalizedName}; }'); - - code.writeln( - 'void ${property.name}(${property.type.cppName} value) ' + - (property.isSetOverride ? 'override' : '') + - '{' - 'if(m_${property.capitalizedName} == value)' - '{return;}' - 'm_${property.capitalizedName} = value;' - '${property.name}Changed();' - '}'); + if (!property.isPureVirtual) { + code.writeln( + 'void ${property.name}(${property.type.cppName} value) ' + + (property.isSetOverride ? 'override' : '') + + '{' + 'if(m_${property.capitalizedName} == value)' + '{return;}' + 'm_${property.capitalizedName} = value;' + '${property.name}Changed();' + '}'); + } else { + code.writeln( + 'virtual void ${property.name}(${property.type.cppName} value) = 0;'); + } } code.writeln(); @@ -466,6 +491,12 @@ class Definition { for (final property in properties) { ctxCode.writeln('case ${property.definition.name}Base' '::${property.name}PropertyKey:'); + if (property.key != null) { + for (final altKey in property.key!.alternates) { + ctxCode.writeln('case ${property.definition.name}Base' + '::${altKey.stringValue}PropertyKey:'); + } + } ctxCode.writeln('object->as<${property.definition.name}Base>()->' '${property.name}(value);'); ctxCode.writeln('break;'); @@ -486,6 +517,10 @@ class Definition { for (final property in properties) { ctxCode.writeln('case ${property.definition.name}Base' '::${property.name}PropertyKey:'); + for (final altKey in property.key!.alternates) { + ctxCode.writeln('case ${property.definition.name}Base' + '::${altKey.stringValue}PropertyKey:'); + } ctxCode .writeln('return object->as<${property.definition.name}Base>()->' '${property.name}();'); @@ -508,6 +543,10 @@ class Definition { for (final property in properties) { ctxCode.writeln('case ${property.definition.name}Base' '::${property.name}PropertyKey:'); + for (final altKey in property.key!.alternates) { + ctxCode.writeln('case ${property.definition.name}Base' + '::${altKey.stringValue}PropertyKey:'); + } } } ctxCode.writeln('return Core${fieldType.capitalizedName}Type::id;'); @@ -537,6 +576,26 @@ class Definition { ctxCode.writeln('default:return false;'); ctxCode.writeln('}}'); + // static bool objectSupportsProperty(Core* object, uint32_t propertyKey) { return true; } + ctxCode.writeln(''' + static bool objectSupportsProperty(Core* object, uint32_t propertyKey) { + switch(propertyKey) {'''); + for (final fieldType in usedFieldTypes.keys) { + var properties = getSetFieldTypes[fieldType]; + if (properties != null) { + for (final property in properties) { + ctxCode.writeln('case ${property.definition.name}Base' + '::${property.name}PropertyKey:'); + for (final altKey in property.key!.alternates) { + ctxCode.writeln('case ${property.definition.name}Base' + '::${altKey.stringValue}PropertyKey:'); + } + ctxCode + .writeln('return object->is<${property.definition.name}Base>();'); + } + } + } + ctxCode.writeln('}return false;}'); ctxCode.writeln('};}'); var output = generatedHppPath; diff --git a/dev/core_generator/lib/src/key.dart b/dev/core_generator/lib/src/key.dart index 8e1a8d0d..ca836847 100644 --- a/dev/core_generator/lib/src/key.dart +++ b/dev/core_generator/lib/src/key.dart @@ -4,6 +4,7 @@ import 'property.dart'; class Key { final String? stringValue; final int? intValue; + final List alternates = []; bool get isMissing => intValue == null; @@ -23,12 +24,36 @@ class Key { } dynamic iv = data['int']; dynamic sv = data['string']; + dynamic av = data['alternates']; if (iv is int && sv is String) { - return Key(sv, iv); + final key = Key(sv, iv); + if (av is List) { + for (final a in av) { + if (a is Map) { + dynamic altiv = a['int']; + dynamic altsv = a['string']; + key.alternates.add(Key(altsv, altiv)); + } + } + } + return key; } return null; } - Map serialize() => - {'int': intValue, 'string': stringValue}; + Map serialize() { + final json = {'int': intValue, 'string': stringValue}; + final altsJson = []; + for (final alt in alternates) { + final altJson = { + 'int': alt.intValue, + 'string': alt.stringValue + }; + altsJson.add(altJson); + } + if (altsJson.isNotEmpty) { + json['alternates'] = altsJson; + } + return json; + } } diff --git a/dev/core_generator/lib/src/property.dart b/dev/core_generator/lib/src/property.dart index e13ae67b..46993b13 100644 --- a/dev/core_generator/lib/src/property.dart +++ b/dev/core_generator/lib/src/property.dart @@ -20,6 +20,8 @@ class Property { bool isSetOverride = false; bool isGetOverride = false; bool isEncoded = false; + bool isBindable = false; + bool isPureVirtual = false; FieldType? typeRuntime; static Property? make( @@ -93,6 +95,14 @@ class Property { if (rt is String) { typeRuntime = FieldType.find(rt); } + dynamic b = data['bindable']; + if (b is bool) { + isBindable = b; + } + dynamic pv = data['pureVirtual']; + if (pv is bool) { + isPureVirtual = pv; + } key = Key.fromJSON(data['key']) ?? Key.forProperty(this); } diff --git a/dev/defs/animation/keyframe_uint.json b/dev/defs/animation/keyframe_uint.json new file mode 100644 index 00000000..2a0fe0eb --- /dev/null +++ b/dev/defs/animation/keyframe_uint.json @@ -0,0 +1,19 @@ +{ + "name": "KeyFrameUint", + "key": { + "int": 450, + "string": "keyframeuint" + }, + "extends": "animation/interpolating_keyframe.json", + "properties": { + "value": { + "type": "uint", + "typeRuntime": "uint", + "initialValue": "0", + "key": { + "int": 631, + "string": "value" + } + } + } +} \ No newline at end of file diff --git a/dev/defs/animation/layer_state.json b/dev/defs/animation/layer_state.json index ce57d57b..b15b793b 100644 --- a/dev/defs/animation/layer_state.json +++ b/dev/defs/animation/layer_state.json @@ -34,6 +34,14 @@ "string": "y" }, "runtime": false + }, + "flags": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 536, + "string": "flags" + } } } } \ No newline at end of file diff --git a/dev/defs/animation/listener_align_target.json b/dev/defs/animation/listener_align_target.json index b34168f1..699b1605 100644 --- a/dev/defs/animation/listener_align_target.json +++ b/dev/defs/animation/listener_align_target.json @@ -16,6 +16,15 @@ "string": "targetid" }, "description": "Identifier used to track the object use as a target fo this listener action." + }, + "preserveOffset": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 541, + "string": "preserveoffset" + }, + "description": "Whether to preserve offset between mouse position and target position." } } } \ No newline at end of file diff --git a/dev/defs/animation/listener_viewmodel_change.json b/dev/defs/animation/listener_viewmodel_change.json new file mode 100644 index 00000000..96ffb5b5 --- /dev/null +++ b/dev/defs/animation/listener_viewmodel_change.json @@ -0,0 +1,38 @@ +{ + "name": "ListenerViewModelChange", + "key": { + "int": 487, + "string": "listenerviewmodelchange" + }, + "extends": "animation/listener_action.json", + "properties": { + "bindablePropertyId": { + "type": "Id", + "key": { + "int": 657, + "string": "bindablepropertyid" + }, + "description": "Id of the bindable property", + "runtime": false + }, + "fromViewModelProperty": { + "type": "bool", + "key": { + "int": 658, + "string": "fromviewmodelproperty" + }, + "description": "Whether it is using another view model property as the value", + "runtime": false + }, + "fromDataBindId": { + "type": "Id", + "initialValue": "Core.missingId", + "key": { + "int": 659, + "string": "fromdatabindid" + }, + "description": "Id of the data bind used if the value is taken from another view model property (only needed if fromViewModelProperty is true)", + "runtime": false + } + } +} \ No newline at end of file diff --git a/dev/defs/animation/nested_bool.json b/dev/defs/animation/nested_bool.json index b55c59a2..31aa257f 100644 --- a/dev/defs/animation/nested_bool.json +++ b/dev/defs/animation/nested_bool.json @@ -10,6 +10,7 @@ "type": "bool", "initialValue": "false", "animates": true, + "pureVirtual": true, "key": { "int": 238, "string": "value" diff --git a/dev/defs/animation/nested_number.json b/dev/defs/animation/nested_number.json index 7315cf12..c845ea4e 100644 --- a/dev/defs/animation/nested_number.json +++ b/dev/defs/animation/nested_number.json @@ -10,6 +10,7 @@ "type": "double", "initialValue": "0", "animates": true, + "pureVirtual": true, "key": { "int": 239, "string": "nestedValue" diff --git a/dev/defs/animation/state_transition.json b/dev/defs/animation/state_transition.json index 072d10e0..3ac429eb 100644 --- a/dev/defs/animation/state_transition.json +++ b/dev/defs/animation/state_transition.json @@ -82,6 +82,15 @@ "string": "interpolatorid" }, "description": "The id of the custom interpolator used when interpolation is Cubic." + }, + "randomWeight": { + "type": "uint", + "initialValue": "1", + "key": { + "int": 537, + "string": "randomweight" + }, + "description": "Weight of the transition in the overall random options" } } } \ No newline at end of file diff --git a/dev/defs/animation/transition_artboard_condition.json b/dev/defs/animation/transition_artboard_condition.json new file mode 100644 index 00000000..5a39c662 --- /dev/null +++ b/dev/defs/animation/transition_artboard_condition.json @@ -0,0 +1,8 @@ +{ + "name": "TransitionArtboardCondition", + "key": { + "int": 497, + "string": "transitionartboardcondition" + }, + "extends": "animation/transition_viewmodel_condition.json" +} \ No newline at end of file diff --git a/dev/defs/animation/transition_comparator.json b/dev/defs/animation/transition_comparator.json new file mode 100644 index 00000000..5d01f6e2 --- /dev/null +++ b/dev/defs/animation/transition_comparator.json @@ -0,0 +1,8 @@ +{ + "name": "TransitionComparator", + "key": { + "int": 477, + "string": "transitioncomparator" + }, + "abstract": true +} \ No newline at end of file diff --git a/dev/defs/animation/transition_condition.json b/dev/defs/animation/transition_condition.json index 83365b50..660dbfcc 100644 --- a/dev/defs/animation/transition_condition.json +++ b/dev/defs/animation/transition_condition.json @@ -1,7 +1,7 @@ { "name": "TransitionCondition", "key": { - "int": 67, + "int": 476, "string": "transitioncondition" }, "abstract": true, @@ -28,17 +28,6 @@ }, "description": "Order value for condition in a transition.", "runtime": false - }, - "inputId": { - "type": "Id", - "typeRuntime": "uint", - "initialValue": "Core.missingId", - "initialValueRuntime": "-1", - "key": { - "int": 155, - "string": "inputid" - }, - "description": "Id of the StateMachineInput referenced." } } } \ No newline at end of file diff --git a/dev/defs/animation/transition_input_condition.json b/dev/defs/animation/transition_input_condition.json new file mode 100644 index 00000000..6a6331f7 --- /dev/null +++ b/dev/defs/animation/transition_input_condition.json @@ -0,0 +1,22 @@ +{ + "name": "TransitionInputCondition", + "key": { + "int": 67, + "string": "transitioninputcondition" + }, + "abstract": true, + "extends": "animation/transition_condition.json", + "properties": { + "inputId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 155, + "string": "inputid" + }, + "description": "Id of the StateMachineInput referenced." + } + } +} \ No newline at end of file diff --git a/dev/defs/animation/transition_property_artboard_comparator.json b/dev/defs/animation/transition_property_artboard_comparator.json new file mode 100644 index 00000000..a9502eba --- /dev/null +++ b/dev/defs/animation/transition_property_artboard_comparator.json @@ -0,0 +1,19 @@ +{ + "name": "TransitionPropertyArtboardComparator", + "key": { + "int": 496, + "string": "transitionpropertyartboardcomparator" + }, + "extends": "animation/transition_property_comparator.json", + "properties": { + "propertyType": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 677, + "string": "propertytype" + }, + "description": "Integer representation of the artboard's property used for condition" + } + } +} \ No newline at end of file diff --git a/dev/defs/animation/transition_property_comparator.json b/dev/defs/animation/transition_property_comparator.json new file mode 100644 index 00000000..ee708a20 --- /dev/null +++ b/dev/defs/animation/transition_property_comparator.json @@ -0,0 +1,9 @@ +{ + "name": "TransitionPropertyComparator", + "key": { + "int": 478, + "string": "transitionpropertycomparator" + }, + "abstract": true, + "extends": "animation/transition_comparator.json" +} \ No newline at end of file diff --git a/dev/defs/animation/transition_property_viewmodel_comparator.json b/dev/defs/animation/transition_property_viewmodel_comparator.json new file mode 100644 index 00000000..94b11fb7 --- /dev/null +++ b/dev/defs/animation/transition_property_viewmodel_comparator.json @@ -0,0 +1,19 @@ +{ + "name": "TransitionPropertyViewModelComparator", + "key": { + "int": 479, + "string": "transitionpropertyviewmodelcomparator" + }, + "extends": "animation/transition_property_comparator.json", + "properties": { + "bindablePropertyId": { + "type": "Id", + "key": { + "int": 646, + "string": "bindablepropertyid" + }, + "description": "Id of the bindable property used as comparator", + "runtime": false + } + } +} \ No newline at end of file diff --git a/dev/defs/animation/transition_trigger_condition.json b/dev/defs/animation/transition_trigger_condition.json index 63c9e494..855dc655 100644 --- a/dev/defs/animation/transition_trigger_condition.json +++ b/dev/defs/animation/transition_trigger_condition.json @@ -4,5 +4,5 @@ "int": 68, "string": "transitiontriggercondition" }, - "extends": "animation/transition_condition.json" + "extends": "animation/transition_input_condition.json" } \ No newline at end of file diff --git a/dev/defs/animation/transition_value_boolean_comparator.json b/dev/defs/animation/transition_value_boolean_comparator.json new file mode 100644 index 00000000..c2ee3bf3 --- /dev/null +++ b/dev/defs/animation/transition_value_boolean_comparator.json @@ -0,0 +1,18 @@ +{ + "name": "TransitionValueBooleanComparator", + "key": { + "int": 481, + "string": "transitionvaluebooleancomparator" + }, + "extends": "animation/transition_value_comparator.json", + "properties": { + "value": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 647, + "string": "value" + } + } + } +} \ No newline at end of file diff --git a/dev/defs/animation/transition_value_color_comparator.json b/dev/defs/animation/transition_value_color_comparator.json new file mode 100644 index 00000000..ec5d41f8 --- /dev/null +++ b/dev/defs/animation/transition_value_color_comparator.json @@ -0,0 +1,18 @@ +{ + "name": "TransitionValueColorComparator", + "key": { + "int": 483, + "string": "transitionvaluecolorcomparator" + }, + "extends": "animation/transition_value_comparator.json", + "properties": { + "value": { + "type": "Color", + "initialValue": "0xFF1D1D1D", + "key": { + "int": 651, + "string": "value" + } + } + } +} \ No newline at end of file diff --git a/dev/defs/animation/transition_value_comparator.json b/dev/defs/animation/transition_value_comparator.json new file mode 100644 index 00000000..d428fadb --- /dev/null +++ b/dev/defs/animation/transition_value_comparator.json @@ -0,0 +1,9 @@ +{ + "name": "TransitionValueComparator", + "key": { + "int": 480, + "string": "transitionvaluecomparator" + }, + "abstract": true, + "extends": "animation/transition_comparator.json" +} \ No newline at end of file diff --git a/dev/defs/animation/transition_value_condition.json b/dev/defs/animation/transition_value_condition.json index 8f5a8834..381326f7 100644 --- a/dev/defs/animation/transition_value_condition.json +++ b/dev/defs/animation/transition_value_condition.json @@ -5,7 +5,7 @@ "string": "transitionvaluecondition" }, "abstract": true, - "extends": "animation/transition_condition.json", + "extends": "animation/transition_input_condition.json", "properties": { "opValue": { "type": "uint", diff --git a/dev/defs/animation/transition_value_enum_comparator.json b/dev/defs/animation/transition_value_enum_comparator.json new file mode 100644 index 00000000..54c364c6 --- /dev/null +++ b/dev/defs/animation/transition_value_enum_comparator.json @@ -0,0 +1,21 @@ +{ + "name": "TransitionValueEnumComparator", + "key": { + "int": 485, + "string": "transitionvalueenumcomparator" + }, + "extends": "animation/transition_value_comparator.json", + "properties": { + "value": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 653, + "string": "value" + }, + "description": "The id of the enum to compare the condition to" + } + } +} \ No newline at end of file diff --git a/dev/defs/animation/transition_value_number_comparator.json b/dev/defs/animation/transition_value_number_comparator.json new file mode 100644 index 00000000..7ad7656d --- /dev/null +++ b/dev/defs/animation/transition_value_number_comparator.json @@ -0,0 +1,18 @@ +{ + "name": "TransitionValueNumberComparator", + "key": { + "int": 484, + "string": "transitionvaluenumbercomparator" + }, + "extends": "animation/transition_value_comparator.json", + "properties": { + "value": { + "type": "double", + "initialValue": "0", + "key": { + "int": 652, + "string": "value" + } + } + } +} \ No newline at end of file diff --git a/dev/defs/animation/transition_value_string_comparator.json b/dev/defs/animation/transition_value_string_comparator.json new file mode 100644 index 00000000..7f7cdd78 --- /dev/null +++ b/dev/defs/animation/transition_value_string_comparator.json @@ -0,0 +1,18 @@ +{ + "name": "TransitionValueStringComparator", + "key": { + "int": 486, + "string": "transitionvaluestringcomparator" + }, + "extends": "animation/transition_value_comparator.json", + "properties": { + "value": { + "type": "String", + "initialValue": "''", + "key": { + "int": 654, + "string": "value" + } + } + } +} \ No newline at end of file diff --git a/dev/defs/animation/transition_viewmodel_condition.json b/dev/defs/animation/transition_viewmodel_condition.json new file mode 100644 index 00000000..714abb19 --- /dev/null +++ b/dev/defs/animation/transition_viewmodel_condition.json @@ -0,0 +1,41 @@ +{ + "name": "TransitionViewModelCondition", + "key": { + "int": 482, + "string": "transitionviewmodelcondition" + }, + "extends": "animation/transition_condition.json", + "properties": { + "leftComparatorId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 648, + "string": "leftcomparatorid" + }, + "description": "The id of the left comaprand to use for this condition" + }, + "rightComparatorId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 649, + "string": "rightcomparatorid" + }, + "description": "The id of the right comaprand to use for this condition" + }, + "opValue": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 650, + "string": "opvalue" + }, + "description": "Integer representation of the StateMachineOp enum." + } + } +} \ No newline at end of file diff --git a/dev/defs/artboard.json b/dev/defs/artboard.json index 8b1f39a0..bb527360 100644 --- a/dev/defs/artboard.json +++ b/dev/defs/artboard.json @@ -4,53 +4,8 @@ "int": 1, "string": "artboard" }, - "extends": "world_transform_component.json", + "extends": "layout_component.json", "properties": { - "clip": { - "type": "bool", - "initialValue": "true", - "key": { - "int": 196, - "string": "clip" - }, - "description": "True when the artboard bounds clip its contents." - }, - "width": { - "type": "double", - "initialValue": "0", - "key": { - "int": 7, - "string": "w" - }, - "description": "Width of the artboard." - }, - "height": { - "type": "double", - "initialValue": "0", - "key": { - "int": 8, - "string": "h" - }, - "description": "Height of the artboard." - }, - "x": { - "type": "double", - "initialValue": "0", - "key": { - "int": 9, - "string": "x" - }, - "description": "X coordinate in editor world space." - }, - "y": { - "type": "double", - "initialValue": "0", - "key": { - "int": 10, - "string": "y" - }, - "description": "Y coordinate in editor world space." - }, "originX": { "type": "double", "initialValue": "0", @@ -130,10 +85,20 @@ "initialValue": "Core.missingId", "initialValueRuntime": "-1", "key": { - "int": 434, + "int": 583, "string": "viewmodelid" }, "description": "The view model attached to this artboard data context." + }, + "viewModelInstanceId": { + "type": "Id", + "initialValue": "Core.missingId", + "key": { + "int": 584, + "string": "viewmodelinstanceid" + }, + "description": "A view model instance attached for editing purposes.", + "runtime": false } } } \ No newline at end of file diff --git a/dev/defs/assets/audio_asset.json b/dev/defs/assets/audio_asset.json index 667cf270..aed1f625 100644 --- a/dev/defs/assets/audio_asset.json +++ b/dev/defs/assets/audio_asset.json @@ -4,5 +4,57 @@ "int": 406, "string": "audioasset" }, - "extends": "assets/file_asset.json" + "extends": "assets/export_audio.json", + "properties": { + "sampleRate": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 473, + "string": "samplerate" + }, + "description": "Sample rate of the source audio file.", + "runtime": false + }, + "channels": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 474, + "string": "channels" + }, + "description": "Channel count of the source audio file.", + "runtime": false + }, + "durationSeconds": { + "type": "double", + "initialValue": "0", + "key": { + "int": 475, + "string": "durationseconds" + }, + "description": "Duration in seconds of the source audio file.", + "runtime": false + }, + "formatValue": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 476, + "string": "formatvalue" + }, + "description": "Container format value of the source audio file.", + "runtime": false + }, + "isCustom": { + "type": "bool", + "initialValue": "true", + "key": { + "int": 426, + "string": "iscustom" + }, + "description": "True if a custom asset, i.e the user has uploaded it", + "runtime": false + } + } } \ No newline at end of file diff --git a/dev/defs/assets/export_audio.json b/dev/defs/assets/export_audio.json new file mode 100644 index 00000000..37c747c6 --- /dev/null +++ b/dev/defs/assets/export_audio.json @@ -0,0 +1,40 @@ +{ + "name": "ExportAudio", + "key": { + "int": 422, + "string": "exportaudio" + }, + "abstract": true, + "extends": "assets/file_asset.json", + "properties": { + "exportFormatValue": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 527, + "string": "formatvalue" + }, + "description": "Start seconds of this clip", + "runtime": false + }, + "exportQualityValue": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 528, + "string": "qualityvalue" + }, + "description": "Start seconds of this clip", + "runtime": false + }, + "volume": { + "type": "double", + "initialValue": "1", + "key": { + "int": 530, + "string": "volume" + }, + "description": "Volume applied to all instances of this audio asset." + } + } +} \ No newline at end of file diff --git a/dev/defs/data_bind/bindable_property.json b/dev/defs/data_bind/bindable_property.json new file mode 100644 index 00000000..dbc57e56 --- /dev/null +++ b/dev/defs/data_bind/bindable_property.json @@ -0,0 +1,20 @@ +{ + "name": "BindableProperty", + "key": { + "int": 9, + "string": "bindableproperty" + }, + "abstract": true, + "properties": { + "dataBindId": { + "type": "Id", + "initialValue": "Core.missingId", + "key": { + "int": 633, + "string": "databindid" + }, + "description": "Id of the data bind binded to this property", + "runtime": false + } + } +} \ No newline at end of file diff --git a/dev/defs/data_bind/bindable_property_boolean.json b/dev/defs/data_bind/bindable_property_boolean.json new file mode 100644 index 00000000..78c24f2d --- /dev/null +++ b/dev/defs/data_bind/bindable_property_boolean.json @@ -0,0 +1,19 @@ +{ + "name": "BindablePropertyBoolean", + "key": { + "int": 472, + "string": "bindablepropertyboolean" + }, + "extends": "data_bind/bindable_property.json", + "properties": { + "propertyValue": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 634, + "string": "propertyvalue" + }, + "description": "A property of type bool that can be used for data binding." + } + } +} \ No newline at end of file diff --git a/dev/defs/data_bind/bindable_property_color.json b/dev/defs/data_bind/bindable_property_color.json new file mode 100644 index 00000000..1dd5134b --- /dev/null +++ b/dev/defs/data_bind/bindable_property_color.json @@ -0,0 +1,19 @@ +{ + "name": "BindablePropertyColor", + "key": { + "int": 475, + "string": "bindablepropertycolor" + }, + "extends": "data_bind/bindable_property.json", + "properties": { + "propertyValue": { + "type": "Color", + "initialValue": "0xFF1D1D1D", + "key": { + "int": 638, + "string": "propertyvalue" + }, + "description": "A property of type int that can be used for data binding." + } + } +} \ No newline at end of file diff --git a/dev/defs/data_bind/bindable_property_enum.json b/dev/defs/data_bind/bindable_property_enum.json new file mode 100644 index 00000000..b3faf032 --- /dev/null +++ b/dev/defs/data_bind/bindable_property_enum.json @@ -0,0 +1,21 @@ +{ + "name": "BindablePropertyEnum", + "key": { + "int": 474, + "string": "bindablepropertyenum" + }, + "extends": "data_bind/bindable_property.json", + "properties": { + "propertyValue": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 637, + "string": "propertyvalue" + }, + "description": "The id of the enum that can be used for data binding." + } + } +} \ No newline at end of file diff --git a/dev/defs/data_bind/bindable_property_number.json b/dev/defs/data_bind/bindable_property_number.json new file mode 100644 index 00000000..a2ab667e --- /dev/null +++ b/dev/defs/data_bind/bindable_property_number.json @@ -0,0 +1,19 @@ +{ + "name": "BindablePropertyNumber", + "key": { + "int": 473, + "string": "bindablepropertynumber" + }, + "extends": "data_bind/bindable_property.json", + "properties": { + "propertyValue": { + "type": "double", + "initialValue": "0", + "key": { + "int": 636, + "string": "propertyvalue" + }, + "description": "A property of type double that can be used for data binding." + } + } +} \ No newline at end of file diff --git a/dev/defs/data_bind/bindable_property_string.json b/dev/defs/data_bind/bindable_property_string.json new file mode 100644 index 00000000..aac63dff --- /dev/null +++ b/dev/defs/data_bind/bindable_property_string.json @@ -0,0 +1,19 @@ +{ + "name": "BindablePropertyString", + "key": { + "int": 471, + "string": "bindablepropertystring" + }, + "extends": "data_bind/bindable_property.json", + "properties": { + "propertyValue": { + "type": "String", + "initialValue": "''", + "key": { + "int": 635, + "string": "propertyvalue" + }, + "description": "A property of type String that can be used for data binding." + } + } +} \ No newline at end of file diff --git a/dev/defs/data_bind/converters/data_converter.json b/dev/defs/data_bind/converters/data_converter.json new file mode 100644 index 00000000..b5a0c49e --- /dev/null +++ b/dev/defs/data_bind/converters/data_converter.json @@ -0,0 +1,30 @@ +{ + "name": "DataConverter", + "key": { + "int": 488, + "string": "dataconverter" + }, + "abstract": true, + "properties": { + "order": { + "type": "FractionalIndex", + "initialValue": "FractionalIndex.invalid", + "initialValueRuntime": "0", + "key": { + "int": 661, + "string": "order" + }, + "description": "Order value for sorting data converters", + "runtime": false + }, + "name": { + "type": "String", + "initialValue": "''", + "key": { + "int": 662, + "string": "name" + }, + "description": "Non-unique identifier, used to give friendly names to data converters." + } + } +} \ No newline at end of file diff --git a/dev/defs/data_bind/converters/data_converter_group.json b/dev/defs/data_bind/converters/data_converter_group.json new file mode 100644 index 00000000..acc05f85 --- /dev/null +++ b/dev/defs/data_bind/converters/data_converter_group.json @@ -0,0 +1,8 @@ +{ + "name": "DataConverterGroup", + "key": { + "int": 499, + "string": "dataconvertergroup" + }, + "extends": "data_bind/converters/data_converter.json" +} \ No newline at end of file diff --git a/dev/defs/data_bind/converters/data_converter_group_item.json b/dev/defs/data_bind/converters/data_converter_group_item.json new file mode 100644 index 00000000..60e95ece --- /dev/null +++ b/dev/defs/data_bind/converters/data_converter_group_item.json @@ -0,0 +1,39 @@ +{ + "name": "DataConverterGroupItem", + "key": { + "int": 498, + "string": "dataconvertergroupitem" + }, + "properties": { + "order": { + "type": "FractionalIndex", + "initialValue": "FractionalIndex.invalid", + "key": { + "int": 678, + "string": "order" + }, + "runtime": false + }, + "converterId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 679, + "string": "converterid" + }, + "description": "Id of the converter" + }, + "groupId": { + "type": "Id", + "initialValue": "Core.missingId", + "key": { + "int": 680, + "string": "groupid" + }, + "description": "Id of the group this item belongs to", + "runtime": false + } + } +} \ No newline at end of file diff --git a/dev/defs/data_bind/converters/data_converter_operation.json b/dev/defs/data_bind/converters/data_converter_operation.json new file mode 100644 index 00000000..1a921799 --- /dev/null +++ b/dev/defs/data_bind/converters/data_converter_operation.json @@ -0,0 +1,28 @@ +{ + "name": "DataConverterOperation", + "key": { + "int": 500, + "string": "dataconverteroperation" + }, + "extends": "data_bind/converters/data_converter.json", + "properties": { + "value": { + "type": "double", + "initialValue": "1", + "key": { + "int": 681, + "string": "value" + }, + "description": "Number to multiply the input to" + }, + "operationType": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 682, + "string": "operationtype" + }, + "description": "The operation tu use for the input and value" + } + } +} \ No newline at end of file diff --git a/dev/defs/data_bind/converters/data_converter_rounder.json b/dev/defs/data_bind/converters/data_converter_rounder.json new file mode 100644 index 00000000..30dfbfd1 --- /dev/null +++ b/dev/defs/data_bind/converters/data_converter_rounder.json @@ -0,0 +1,20 @@ +{ + "name": "DataConverterRounder", + "key": { + "int": 489, + "string": "dataconverterrounder" + }, + "extends": "data_bind/converters/data_converter.json", + "properties": { + "decimals": { + "type": "uint", + "typeRuntime": "uint", + "initialValue": "0", + "key": { + "int": 669, + "string": "decimals" + }, + "description": "Number of decimals to round to" + } + } +} \ No newline at end of file diff --git a/dev/defs/data_bind/converters/data_converter_to_string.json b/dev/defs/data_bind/converters/data_converter_to_string.json new file mode 100644 index 00000000..5584ce4e --- /dev/null +++ b/dev/defs/data_bind/converters/data_converter_to_string.json @@ -0,0 +1,8 @@ +{ + "name": "DataConverterToString", + "key": { + "int": 490, + "string": "dataconvertertostring" + }, + "extends": "data_bind/converters/data_converter.json" +} \ No newline at end of file diff --git a/dev/defs/data_bind/data_bind.json b/dev/defs/data_bind/data_bind.json new file mode 100644 index 00000000..3ec525b6 --- /dev/null +++ b/dev/defs/data_bind/data_bind.json @@ -0,0 +1,47 @@ +{ + "name": "DataBind", + "key": { + "int": 446, + "string": "databind" + }, + "properties": { + "targetId": { + "type": "Id", + "initialValue": "Core.missingId", + "key": { + "int": 585, + "string": "targetid" + }, + "description": "Identifier used to track the object that is targetted.", + "runtime": false + }, + "propertyKey": { + "type": "uint", + "initialValue": "CoreContext.invalidPropertyKey", + "key": { + "int": 586, + "string": "propertykey" + }, + "description": "The property that is targeted." + }, + "flags": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 587, + "string": "flags" + } + }, + "converterId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 660, + "string": "converterid" + }, + "description": "Identifier used to link to a data converter." + } + } +} \ No newline at end of file diff --git a/dev/defs/data_bind/data_bind_context.json b/dev/defs/data_bind/data_bind_context.json new file mode 100644 index 00000000..d9e04e6a --- /dev/null +++ b/dev/defs/data_bind/data_bind_context.json @@ -0,0 +1,21 @@ +{ + "name": "DataBindContext", + "key": { + "int": 447, + "string": "databindcontext" + }, + "extends": "data_bind/data_bind.json", + "properties": { + "sourcePathIds": { + "type": "List", + "typeRuntime": "Bytes", + "encoded": true, + "initialValue": "[]", + "key": { + "int": 588, + "string": "sourcepathids" + }, + "description": "Path to the selected property." + } + } +} \ No newline at end of file diff --git a/dev/defs/layout/axis.json b/dev/defs/layout/axis.json new file mode 100644 index 00000000..0703844b --- /dev/null +++ b/dev/defs/layout/axis.json @@ -0,0 +1,29 @@ +{ + "name": "Axis", + "key": { + "int": 492, + "string": "axis" + }, + "abstract": true, + "extends": "component.json", + "properties": { + "offset": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 675, + "string": "offset" + } + }, + "normalized": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 676, + "string": "normalized" + }, + "description": "true means offset indicates a percentage" + } + } +} \ No newline at end of file diff --git a/dev/defs/layout/axis_x.json b/dev/defs/layout/axis_x.json new file mode 100644 index 00000000..47774c7b --- /dev/null +++ b/dev/defs/layout/axis_x.json @@ -0,0 +1,8 @@ +{ + "name": "AxisX", + "key": { + "int": 495, + "string": "axisx" + }, + "extends": "layout/axis.json" +} \ No newline at end of file diff --git a/dev/defs/layout/axis_y.json b/dev/defs/layout/axis_y.json new file mode 100644 index 00000000..b3bd16e5 --- /dev/null +++ b/dev/defs/layout/axis_y.json @@ -0,0 +1,8 @@ +{ + "name": "AxisY", + "key": { + "int": 494, + "string": "axisy" + }, + "extends": "layout/axis.json" +} \ No newline at end of file diff --git a/dev/defs/layout/layout_component_style.json b/dev/defs/layout/layout_component_style.json new file mode 100644 index 00000000..d96f7afb --- /dev/null +++ b/dev/defs/layout/layout_component_style.json @@ -0,0 +1,779 @@ +{ + "name": "LayoutComponentStyle", + "key": { + "int": 420, + "string": "layoutcomponentstyle" + }, + "extends": "component.json", + "properties": { + "gapHorizontal": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutgap", + "key": { + "int": 498, + "string": "gaphorizontal" + }, + "description": "Horizontal gap between children in layout component" + }, + "gapVertical": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutgap", + "key": { + "int": 499, + "string": "gapvertical" + }, + "description": "Vertical gap between children in layout component" + }, + "maxWidth": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutmax", + "key": { + "int": 500, + "string": "maxwidth" + }, + "description": "Max width of the item." + }, + "maxHeight": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutmax", + "key": { + "int": 501, + "string": "maxheight" + }, + "description": "Max height of the item." + }, + "minWidth": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutmin", + "key": { + "int": 502, + "string": "minwidth" + }, + "description": "Min width of the item." + }, + "minHeight": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutmin", + "key": { + "int": 503, + "string": "minheight" + }, + "description": "Min height of the item." + }, + "borderLeft": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutborder", + "key": { + "int": 504, + "string": "borderleft" + }, + "description": "Left border value." + }, + "borderRight": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutborder", + "key": { + "int": 505, + "string": "borderright" + }, + "description": "Right border value." + }, + "borderTop": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutborder", + "key": { + "int": 506, + "string": "bordertop" + }, + "description": "Top border value." + }, + "borderBottom": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutborder", + "key": { + "int": 507, + "string": "borderbottom" + }, + "description": "Bottom border value." + }, + "marginLeft": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutmargin", + "key": { + "int": 508, + "string": "marginleft" + }, + "description": "Left margin value." + }, + "marginRight": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutmargin", + "key": { + "int": 509, + "string": "marginright" + }, + "description": "Right margin value." + }, + "marginTop": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutmargin", + "key": { + "int": 510, + "string": "margintop" + }, + "description": "Top margin value." + }, + "marginBottom": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutmargin", + "key": { + "int": 511, + "string": "marginbottom" + }, + "description": "Bottom margin value." + }, + "paddingLeft": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutpadding", + "key": { + "int": 512, + "string": "paddingleft" + }, + "description": "Left padding value." + }, + "paddingRight": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutpadding", + "key": { + "int": 513, + "string": "paddingright" + }, + "description": "Right padding value." + }, + "paddingTop": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutpadding", + "key": { + "int": 514, + "string": "paddingtop" + }, + "description": "Top padding value." + }, + "paddingBottom": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutpadding", + "key": { + "int": 515, + "string": "paddingbottom" + }, + "description": "Bottom padding value." + }, + "positionLeft": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutposition", + "key": { + "int": 516, + "string": "positionleft" + }, + "description": "Left position value." + }, + "positionRight": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutposition", + "key": { + "int": 517, + "string": "positionright" + }, + "description": "Right position value." + }, + "positionTop": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutposition", + "key": { + "int": 518, + "string": "positiontop" + }, + "description": "Top position value." + }, + "positionBottom": { + "type": "double", + "initialValue": "0", + "animates": true, + "group": "layoutposition", + "key": { + "int": 519, + "string": "positionbottom" + }, + "description": "Bottom position value." + }, + "flex": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 520, + "string": "flex" + }, + "description": "Flex value." + }, + "flexGrow": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 521, + "string": "flexgrow" + }, + "description": "Flex grow value." + }, + "flexShrink": { + "type": "double", + "initialValue": "1", + "animates": true, + "key": { + "int": 522, + "string": "flexshrink" + }, + "description": "Flex shrink value." + }, + "flexBasis": { + "type": "double", + "initialValue": "1", + "animates": true, + "key": { + "int": 523, + "string": "flexbasis" + }, + "description": "Flex basis value." + }, + "aspectRatio": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 524, + "string": "aspectratio" + }, + "description": "Aspect ratio value." + }, + "showUnitToggle": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 542, + "string": "showunittoggle" + }, + "description": "Show layout unit toggle in inspector position widget. Editor only.", + "runtime": false + }, + "edgeConstraints": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 545, + "string": "edgeconstraints" + }, + "runtime": false + }, + "layoutWidthScaleType": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 655, + "string": "layoutwidthscaletype" + } + }, + "layoutHeightScaleType": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 656, + "string": "layoutheightscaletype" + } + }, + "layoutAlignmentType": { + "type": "uint", + "initialValue": "0", + "animates": true, + "key": { + "int": 632, + "string": "layoutalignmenttype" + } + }, + "animationStyleType": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 589, + "string": "animationstyletype" + }, + "description": "The type of animation none|custom|inherit applied to this layout." + }, + "interpolationType": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 590, + "string": "interpolationtype" + }, + "description": "The type of interpolation index in KeyframeInterpolation applied to this layout." + }, + "interpolatorId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 591, + "string": "interpolatorid" + }, + "description": "The id of the custom interpolator used when interpolation is Cubic." + }, + "interpolationTime": { + "type": "double", + "initialValue": "0", + "key": { + "int": 592, + "string": "interpolationtime" + }, + "description": "The time over which the interpolator applies." + }, + "displayValue": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 596, + "string": "displayvalue" + }, + "description": "" + }, + "positionTypeValue": { + "type": "uint", + "initialValue": "1", + "key": { + "int": 597, + "string": "positiontypevalue" + }, + "description": "" + }, + "flexDirectionValue": { + "type": "uint", + "initialValue": "2", + "animates": true, + "key": { + "int": 598, + "string": "flexdirectionvalue" + }, + "description": "Flex dir" + }, + "directionValue": { + "type": "uint", + "initialValue": "0", + "animates": true, + "key": { + "int": 599, + "string": "directionvalue" + }, + "description": "" + }, + "alignContentValue": { + "type": "uint", + "initialValue": "0", + "animates": true, + "key": { + "int": 600, + "string": "aligncontentvalue" + }, + "description": "" + }, + "alignItemsValue": { + "type": "uint", + "initialValue": "1", + "animates": true, + "key": { + "int": 601, + "string": "alignitemsvalue" + }, + "description": "" + }, + "alignSelfValue": { + "type": "uint", + "initialValue": "0", + "animates": true, + "key": { + "int": 602, + "string": "alignselfvalue" + }, + "description": "" + }, + "justifyContentValue": { + "type": "uint", + "initialValue": "0", + "animates": true, + "key": { + "int": 603, + "string": "justifycontentvalue" + }, + "description": "" + }, + "flexWrapValue": { + "type": "uint", + "initialValue": "0", + "animates": true, + "key": { + "int": 604, + "string": "flexwrapvalue" + }, + "description": "" + }, + "overflowValue": { + "type": "uint", + "initialValue": "0", + "animates": true, + "key": { + "int": 605, + "string": "overflowvalue" + }, + "description": "" + }, + "intrinsicallySizedValue": { + "type": "bool", + "initialValue": "false", + "animates": true, + "key": { + "int": 606, + "string": "intrinsicallysizedvalue" + }, + "description": "" + }, + "widthUnitsValue": { + "type": "uint", + "initialValue": "1", + "key": { + "int": 607, + "string": "widthunitsvalue" + }, + "description": "" + }, + "heightUnitsValue": { + "type": "uint", + "initialValue": "1", + "key": { + "int": 608, + "string": "heightunitsvalue" + }, + "description": "" + }, + "borderLeftUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutborder", + "key": { + "int": 609, + "string": "borderleftunitsvalue" + }, + "description": "" + }, + "borderRightUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutborder", + "key": { + "int": 610, + "string": "borderrightunitsvalue" + }, + "description": "" + }, + "borderTopUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutborder", + "key": { + "int": 611, + "string": "bordertopunitsvalue" + }, + "description": "" + }, + "borderBottomUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutborder", + "key": { + "int": 612, + "string": "borderbottomunitsvalue" + }, + "description": "" + }, + "marginLeftUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutmargin", + "key": { + "int": 613, + "string": "marginleftunitsvalue" + }, + "description": "" + }, + "marginRightUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutmargin", + "key": { + "int": 614, + "string": "marginrightunitsvalue" + }, + "description": "" + }, + "marginTopUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutmargin", + "key": { + "int": 615, + "string": "margintopunitsvalue" + }, + "description": "" + }, + "marginBottomUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutmargin", + "key": { + "int": 616, + "string": "marginbottomunitsvalue" + }, + "description": "" + }, + "paddingLeftUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutpadding", + "key": { + "int": 617, + "string": "paddingleftunitsvalue" + }, + "description": "" + }, + "paddingRightUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutpadding", + "key": { + "int": 618, + "string": "paddingrightunitsvalue" + }, + "description": "" + }, + "paddingTopUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutpadding", + "key": { + "int": 619, + "string": "paddingtopunitsvalue" + }, + "description": "" + }, + "paddingBottomUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutpadding", + "key": { + "int": 620, + "string": "paddingbottomunitsvalue" + }, + "description": "" + }, + "positionLeftUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutposition", + "key": { + "int": 621, + "string": "positionleftunitsvalue" + }, + "description": "" + }, + "positionRightUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutposition", + "key": { + "int": 622, + "string": "positionrightunitsvalue" + }, + "description": "" + }, + "positionTopUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutposition", + "key": { + "int": 623, + "string": "positiontopunitsvalue" + }, + "description": "" + }, + "positionBottomUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutposition", + "key": { + "int": 624, + "string": "positionbottomunitsvalue" + }, + "description": "" + }, + "gapHorizontalUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutgap", + "key": { + "int": 625, + "string": "gaphorizontalunitsvalue" + }, + "description": "" + }, + "gapVerticalUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutgap", + "key": { + "int": 626, + "string": "gapverticalunitsvalue" + }, + "description": "" + }, + "minWidthUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutmin", + "key": { + "int": 627, + "string": "minwidthunitsvalue" + }, + "description": "" + }, + "minHeightUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutmin", + "key": { + "int": 628, + "string": "minheightunitsvalue" + }, + "description": "" + }, + "maxWidthUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutmax", + "key": { + "int": 629, + "string": "maxwidthunitsvalue" + }, + "description": "" + }, + "maxHeightUnitsValue": { + "type": "uint", + "initialValue": "0", + "group": "layoutmax", + "key": { + "int": 630, + "string": "maxheightunitsvalue" + }, + "description": "" + }, + "linkCornerRadius": { + "type": "bool", + "initialValue": "true", + "key": { + "int": 639, + "string": "linkcornerradius" + }, + "description": "Whether the TL corner radius defines all the radiuses" + }, + "cornerRadiusTL": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 640, + "string": "cornerradiustl" + }, + "description": "Top left radius of the corners of this layout" + }, + "cornerRadiusTR": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 641, + "string": "cornerradiustr" + }, + "description": "Top right radius of the corners of this layout" + }, + "cornerRadiusBL": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 642, + "string": "cornerradiusbl" + }, + "description": "Bottom left radius of the corners of this layout" + }, + "cornerRadiusBR": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 643, + "string": "cornerradiusbr" + }, + "description": "Bottom right radius of the corners of this layout" + } + } +} \ No newline at end of file diff --git a/dev/defs/layout/n_slicer.json b/dev/defs/layout/n_slicer.json new file mode 100644 index 00000000..c244b37c --- /dev/null +++ b/dev/defs/layout/n_slicer.json @@ -0,0 +1,8 @@ +{ + "name": "NSlicer", + "key": { + "int": 493, + "string": "nslicer" + }, + "extends": "container_component.json" +} \ No newline at end of file diff --git a/dev/defs/layout/n_slicer_tile_mode.json b/dev/defs/layout/n_slicer_tile_mode.json new file mode 100644 index 00000000..fae93886 --- /dev/null +++ b/dev/defs/layout/n_slicer_tile_mode.json @@ -0,0 +1,48 @@ +{ + "name": "NSlicerTileMode", + "key": { + "int": 491, + "string": "nslicertilemode" + }, + "extends": "component.json", + "properties": { + "patchX": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 670, + "string": "patchx" + }, + "description": "the x index of the patch to style", + "runtime": false + }, + "patchY": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 671, + "string": "patchy" + }, + "description": "the y index of the patch to style", + "runtime": false + }, + "patchIndex": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 672, + "string": "patchindex" + }, + "description": "the index of the patch to style, non-negative" + }, + "style": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 673, + "string": "style" + }, + "description": "represents stretch, repeat, hidden, etc." + } + } +} \ No newline at end of file diff --git a/dev/defs/layout_component.json b/dev/defs/layout_component.json new file mode 100644 index 00000000..8aca893a --- /dev/null +++ b/dev/defs/layout_component.json @@ -0,0 +1,50 @@ +{ + "name": "LayoutComponent", + "key": { + "int": 409, + "string": "layoutcomponent" + }, + "extends": "drawable.json", + "properties": { + "clip": { + "type": "bool", + "initialValue": "true", + "key": { + "int": 196, + "string": "clip" + }, + "description": "True when the layout component bounds clip its contents." + }, + "width": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 7, + "string": "w" + }, + "description": "Initial width of the item." + }, + "height": { + "type": "double", + "initialValue": "0", + "animates": true, + "key": { + "int": 8, + "string": "h" + }, + "description": "Initial height of the item." + }, + "styleId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 494, + "string": "styleid" + }, + "description": "LayoutStyle that defines the styling for this LayoutComponent" + } + } +} \ No newline at end of file diff --git a/dev/defs/nested_artboard.json b/dev/defs/nested_artboard.json index abe086a4..8a1ea487 100644 --- a/dev/defs/nested_artboard.json +++ b/dev/defs/nested_artboard.json @@ -16,6 +16,17 @@ "string": "artboardid" }, "description": "Identifier used to track the Artboard nested." + }, + "dataBindPathIds": { + "type": "List", + "typeRuntime": "Bytes", + "encoded": true, + "initialValue": "[]", + "key": { + "int": 582, + "string": "databindpathids" + }, + "description": "Path to the selected property." } } } \ No newline at end of file diff --git a/dev/defs/nested_artboard_layout.json b/dev/defs/nested_artboard_layout.json new file mode 100644 index 00000000..4730f94f --- /dev/null +++ b/dev/defs/nested_artboard_layout.json @@ -0,0 +1,66 @@ +{ + "name": "NestedArtboardLayout", + "key": { + "int": 452, + "string": "nestedartboardlayout" + }, + "extends": "nested_artboard.json", + "properties": { + "instanceWidth": { + "type": "double", + "initialValue": "-1", + "animates": true, + "key": { + "int": 663, + "string": "instancewidth" + }, + "description": "Width value in points or percent of this nested artboard instance." + }, + "instanceHeight": { + "type": "double", + "initialValue": "-1", + "animates": true, + "key": { + "int": 664, + "string": "instanceheight" + }, + "description": "Height value in points or percent of this nested artboard instance" + }, + "instanceWidthUnitsValue": { + "type": "uint", + "initialValue": "1", + "key": { + "int": 665, + "string": "instancewidthunitsvalue" + }, + "description": "Whether to display width in points or percent" + }, + "instanceHeightUnitsValue": { + "type": "uint", + "initialValue": "1", + "key": { + "int": 666, + "string": "instanceheightunitsvalue" + }, + "description": "Whether to display height in points or percent" + }, + "instanceWidthScaleType": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 667, + "string": "instancewidthscaletype" + }, + "description": "Width scale type fixed | fill" + }, + "instanceHeightScaleType": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 668, + "string": "instanceheightscaletype" + }, + "description": "Height scale type fixed | fill" + } + } +} \ No newline at end of file diff --git a/dev/defs/nested_artboard_leaf.json b/dev/defs/nested_artboard_leaf.json new file mode 100644 index 00000000..cddf1585 --- /dev/null +++ b/dev/defs/nested_artboard_leaf.json @@ -0,0 +1,34 @@ +{ + "name": "NestedArtboardLeaf", + "key": { + "int": 451, + "string": "nested_artboard_leaf" + }, + "extends": "nested_artboard.json", + "properties": { + "fit": { + "type": "uint", + "key": { + "int": 538, + "string": "fit" + }, + "description": "Fit type for the nested artboard's runtime artboard." + }, + "alignmentX": { + "type": "double", + "key": { + "int": 644, + "string": "alignmentx" + }, + "description": "Alignment value on X." + }, + "alignmentY": { + "type": "double", + "key": { + "int": 645, + "string": "alignmenty" + }, + "description": "Alignment value on Y." + } + } +} \ No newline at end of file diff --git a/dev/defs/node.json b/dev/defs/node.json index f604980a..36a1ea32 100644 --- a/dev/defs/node.json +++ b/dev/defs/node.json @@ -14,8 +14,15 @@ "group": "position", "key": { "int": 13, - "string": "x" - } + "string": "x", + "alternates": [ + { + "int": 9, + "string": "xArtboard" + } + ] + }, + "bindable": true }, "y": { "type": "double", @@ -25,8 +32,15 @@ "group": "position", "key": { "int": 14, - "string": "y" - } + "string": "y", + "alternates": [ + { + "int": 10, + "string": "yArtboard" + } + ] + }, + "bindable": true }, "styleValue": { "type": "uint", diff --git a/dev/defs/viewmodel/data_enum.json b/dev/defs/viewmodel/data_enum.json new file mode 100644 index 00000000..c65b2522 --- /dev/null +++ b/dev/defs/viewmodel/data_enum.json @@ -0,0 +1,40 @@ +{ + "name": "DataEnum", + "key": { + "int": 438, + "string": "dataenum" + }, + "properties": { + "order": { + "type": "FractionalIndex", + "initialValue": "FractionalIndex.invalid", + "initialValueRuntime": "0", + "key": { + "int": 570, + "string": "order" + }, + "description": "Order value for sorting data enums.", + "runtime": false + }, + "splitKeyValues": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 571, + "string": "splitkeyvalues" + }, + "description": "Whether the user can edit keys and values separately.", + "runtime": false + }, + "name": { + "type": "String", + "initialValue": "''", + "key": { + "int": 572, + "string": "name" + }, + "description": "Non-unique identifier, used to give friendly names to enums.", + "runtime": false + } + } +} \ No newline at end of file diff --git a/dev/defs/viewmodel/data_enum_value.json b/dev/defs/viewmodel/data_enum_value.json new file mode 100644 index 00000000..d597785f --- /dev/null +++ b/dev/defs/viewmodel/data_enum_value.json @@ -0,0 +1,48 @@ +{ + "name": "DataEnumValue", + "key": { + "int": 445, + "string": "dataenumvalue" + }, + "properties": { + "key": { + "type": "String", + "initialValue": "''", + "key": { + "int": 578, + "string": "key" + }, + "description": "The key of this key value enum pair." + }, + "value": { + "type": "String", + "initialValue": "''", + "key": { + "int": 579, + "string": "value" + }, + "description": "The value of this key value enum pair." + }, + "enumId": { + "type": "Id", + "initialValue": "Core.missingId", + "key": { + "int": 580, + "string": "enumid" + }, + "description": "The id of the enum property this value belongs to.", + "runtime": false + }, + "order": { + "type": "FractionalIndex", + "initialValue": "FractionalIndex.invalid", + "initialValueRuntime": "0", + "key": { + "int": 581, + "string": "order" + }, + "description": "Order value for sorting enum values.", + "runtime": false + } + } +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel.json b/dev/defs/viewmodel/viewmodel.json new file mode 100644 index 00000000..bff9a160 --- /dev/null +++ b/dev/defs/viewmodel/viewmodel.json @@ -0,0 +1,32 @@ +{ + "name": "ViewModel", + "key": { + "int": 435, + "string": "viewmodel" + }, + "extends": "viewmodel/viewmodel_component.json", + "properties": { + "viewModelOrder": { + "type": "FractionalIndex", + "initialValue": "FractionalIndex.invalid", + "initialValueRuntime": "0", + "key": { + "int": 563, + "string": "viewmodelorder" + }, + "description": "Order value for sorting View Models.", + "runtime": false + }, + "defaultInstanceId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 564, + "string": "defaultinstanceid" + }, + "description": "The default instance attached to the view model." + } + } +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_component.json b/dev/defs/viewmodel/viewmodel_component.json new file mode 100644 index 00000000..fd68e1ef --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_component.json @@ -0,0 +1,18 @@ +{ + "name": "ViewModelComponent", + "key": { + "int": 429, + "string": "viewmodelcomponent" + }, + "properties": { + "name": { + "type": "String", + "initialValue": "''", + "key": { + "int": 557, + "string": "name" + }, + "description": "Non-unique identifier, used to give friendly names to any view model component." + } + } +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_instance.json b/dev/defs/viewmodel/viewmodel_instance.json new file mode 100644 index 00000000..1ac44472 --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_instance.json @@ -0,0 +1,50 @@ +{ + "name": "ViewModelInstance", + "key": { + "int": 437, + "string": "viewmodelinstance" + }, + "extends": "component.json", + "properties": { + "viewModelId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "0", + "key": { + "int": 566, + "string": "viewmodelid" + } + }, + "x": { + "type": "double", + "initialValue": "0", + "key": { + "int": 567, + "string": "x" + }, + "description": "The x coordinate of the stage representation.", + "runtime": false + }, + "y": { + "type": "double", + "initialValue": "0", + "key": { + "int": 568, + "string": "y" + }, + "description": "The y coordinate of the stage representation.", + "runtime": false + }, + "onStage": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 569, + "string": "onstage" + }, + "description": "Whether it is visible on stage or not.", + "runtime": false + } + } +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_instance_boolean.json b/dev/defs/viewmodel/viewmodel_instance_boolean.json new file mode 100644 index 00000000..a3ea1d57 --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_instance_boolean.json @@ -0,0 +1,29 @@ +{ + "name": "ViewModelInstanceBoolean", + "key": { + "int": 449, + "string": "viewmodelinstanceboolean" + }, + "extends": "viewmodel/viewmodel_instance_value.json", + "properties": { + "propertyValue": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 593, + "string": "propertyvalue" + }, + "description": "The boolean value." + }, + "playbackValue": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 594, + "string": "playbackvalue" + }, + "runtime": false, + "coop": false + } + } +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_instance_color.json b/dev/defs/viewmodel/viewmodel_instance_color.json new file mode 100644 index 00000000..23ae5df0 --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_instance_color.json @@ -0,0 +1,29 @@ +{ + "name": "ViewModelInstanceColor", + "key": { + "int": 426, + "string": "viewmodelinstancecolor" + }, + "extends": "viewmodel/viewmodel_instance_value.json", + "properties": { + "propertyValue": { + "type": "Color", + "initialValue": "0xFF1D1D1D", + "key": { + "int": 555, + "string": "propertyvalue" + }, + "description": "The color value" + }, + "playbackValue": { + "type": "Color", + "initialValue": "0xFF1D1D1D", + "key": { + "int": 556, + "string": "playbackvalue" + }, + "runtime": false, + "coop": false + } + } +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_instance_enum.json b/dev/defs/viewmodel/viewmodel_instance_enum.json new file mode 100644 index 00000000..7dd93df5 --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_instance_enum.json @@ -0,0 +1,21 @@ +{ + "name": "ViewModelInstanceEnum", + "key": { + "int": 432, + "string": "viewmodelinstanceenum" + }, + "extends": "viewmodel/viewmodel_instance_value.json", + "properties": { + "propertyValue": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "0", + "key": { + "int": 560, + "string": "propertyvalue" + }, + "description": "The id of the enum value." + } + } +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_instance_list.json b/dev/defs/viewmodel/viewmodel_instance_list.json new file mode 100644 index 00000000..d38e669a --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_instance_list.json @@ -0,0 +1,8 @@ +{ + "name": "ViewModelInstanceList", + "key": { + "int": 441, + "string": "viewmodelinstancelist" + }, + "extends": "viewmodel/viewmodel_instance_value.json" +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_instance_list_item.json b/dev/defs/viewmodel/viewmodel_instance_list_item.json new file mode 100644 index 00000000..30938fb2 --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_instance_list_item.json @@ -0,0 +1,71 @@ +{ + "name": "ViewModelInstanceListItem", + "key": { + "int": 427, + "string": "viewmodelinstancelistitem" + }, + "properties": { + "useLinkedArtboard": { + "type": "bool", + "initialValue": "true", + "key": { + "int": 547, + "string": "uselinkedartboard" + }, + "description": "Whether the artboard linked to the view model should be used." + }, + "instanceListId": { + "type": "Id", + "initialValue": "Core.missingId", + "key": { + "int": 548, + "string": "instancelistid" + }, + "description": "The id of the list it belongs to.", + "runtime": false + }, + "viewModelId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 549, + "string": "viewmodelid" + }, + "description": "The view model id." + }, + "viewModelInstanceId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 550, + "string": "viewmodelinstanceid" + }, + "description": "The view model instance id." + }, + "artboardId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 551, + "string": "artboardid" + }, + "description": "The artboard id to link the viewmodel to if implicit is set to false." + }, + "order": { + "type": "FractionalIndex", + "initialValue": "FractionalIndex.invalid", + "key": { + "int": 552, + "string": "order" + }, + "description": "The order position in the list.", + "runtime": false + } + } +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_instance_number.json b/dev/defs/viewmodel/viewmodel_instance_number.json new file mode 100644 index 00000000..e0637f9e --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_instance_number.json @@ -0,0 +1,29 @@ +{ + "name": "ViewModelInstanceNumber", + "key": { + "int": 442, + "string": "viewmodelinstancenumber" + }, + "extends": "viewmodel/viewmodel_instance_value.json", + "properties": { + "propertyValue": { + "type": "double", + "initialValue": "0", + "key": { + "int": 575, + "string": "propertyvalue" + }, + "description": "The number value." + }, + "playbackValue": { + "type": "double", + "initialValue": "0", + "key": { + "int": 576, + "string": "playbackvalue" + }, + "runtime": false, + "coop": false + } + } +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_instance_string.json b/dev/defs/viewmodel/viewmodel_instance_string.json new file mode 100644 index 00000000..bd12abb9 --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_instance_string.json @@ -0,0 +1,29 @@ +{ + "name": "ViewModelInstanceString", + "key": { + "int": 433, + "string": "viewmodelinstancestring" + }, + "extends": "viewmodel/viewmodel_instance_value.json", + "properties": { + "propertyValue": { + "type": "String", + "initialValue": "''", + "key": { + "int": 561, + "string": "propertyvalue" + }, + "description": "The string value." + }, + "playbackValue": { + "type": "String", + "initialValue": "''", + "key": { + "int": 562, + "string": "playbackvalue" + }, + "runtime": false, + "coop": false + } + } +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_instance_value.json b/dev/defs/viewmodel/viewmodel_instance_value.json new file mode 100644 index 00000000..df37f837 --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_instance_value.json @@ -0,0 +1,33 @@ +{ + "name": "ViewModelInstanceValue", + "key": { + "int": 428, + "string": "viewmodelinstancevalue" + }, + "abstract": true, + "properties": { + "viewModelInstanceId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "0", + "key": { + "int": 553, + "string": "viewmodelinstanceid" + }, + "description": "Identifier of the view model instance this value is for, at runtime this is expected in context (after) a ViewModelInstance object.", + "runtime": false + }, + "viewModelPropertyId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "0", + "key": { + "int": 554, + "string": "viewmodelpropertyid" + }, + "description": "Identifier of the property this value will provide data for. At runtime these ids are normalized relative to the ViewModel itself." + } + } +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_instance_viewmodel.json b/dev/defs/viewmodel/viewmodel_instance_viewmodel.json new file mode 100644 index 00000000..e156b148 --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_instance_viewmodel.json @@ -0,0 +1,21 @@ +{ + "name": "ViewModelInstanceViewModel", + "key": { + "int": 444, + "string": "viewmodelinstanceviewmodel" + }, + "extends": "viewmodel/viewmodel_instance_value.json", + "properties": { + "propertyValue": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "0", + "key": { + "int": 577, + "string": "propertyvalue" + }, + "description": "The id of the viewmodel instance." + } + } +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_property.json b/dev/defs/viewmodel/viewmodel_property.json new file mode 100644 index 00000000..5ee24829 --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_property.json @@ -0,0 +1,33 @@ +{ + "name": "ViewModelProperty", + "key": { + "int": 430, + "string": "viewmodelproperty" + }, + "extends": "viewmodel/viewmodel_component.json", + "properties": { + "viewModelId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "0", + "key": { + "int": 558, + "string": "viewmodelid" + }, + "description": "Identifier used to track parent View Model.", + "runtime": false + }, + "propertyOrder": { + "type": "FractionalIndex", + "initialValue": "FractionalIndex.invalid", + "initialValueRuntime": "0", + "key": { + "int": 559, + "string": "propertyorder" + }, + "description": "Order value for sorting child elements in View Model.", + "runtime": false + } + } +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_property_boolean.json b/dev/defs/viewmodel/viewmodel_property_boolean.json new file mode 100644 index 00000000..a7703463 --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_property_boolean.json @@ -0,0 +1,8 @@ +{ + "name": "ViewModelPropertyBoolean", + "key": { + "int": 448, + "string": "viewmodelpropertyboolean" + }, + "extends": "viewmodel/viewmodel_property.json" +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_property_color.json b/dev/defs/viewmodel/viewmodel_property_color.json new file mode 100644 index 00000000..c64d0ff8 --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_property_color.json @@ -0,0 +1,8 @@ +{ + "name": "ViewModelPropertyColor", + "key": { + "int": 440, + "string": "viewmodelpropertycolor" + }, + "extends": "viewmodel/viewmodel_property.json" +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_property_enum.json b/dev/defs/viewmodel/viewmodel_property_enum.json new file mode 100644 index 00000000..2706486f --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_property_enum.json @@ -0,0 +1,31 @@ +{ + "name": "ViewModelPropertyEnum", + "key": { + "int": 439, + "string": "viewmodelpropertyenum" + }, + "extends": "viewmodel/viewmodel_property.json", + "properties": { + "splitKeyValues": { + "type": "bool", + "initialValue": "false", + "key": { + "int": 573, + "string": "splitkeyvalues" + }, + "description": "Whether the user can edit keys and values separately.", + "runtime": false + }, + "enumId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 574, + "string": "enumid" + }, + "description": "The id of the enum." + } + } +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_property_list.json b/dev/defs/viewmodel/viewmodel_property_list.json new file mode 100644 index 00000000..1fa6b0ef --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_property_list.json @@ -0,0 +1,8 @@ +{ + "name": "ViewModelPropertyList", + "key": { + "int": 434, + "string": "viewmodelpropertylist" + }, + "extends": "viewmodel/viewmodel_property.json" +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_property_number.json b/dev/defs/viewmodel/viewmodel_property_number.json new file mode 100644 index 00000000..ef9cced7 --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_property_number.json @@ -0,0 +1,8 @@ +{ + "name": "ViewModelPropertyNumber", + "key": { + "int": 431, + "string": "viewmodelpropertynumber" + }, + "extends": "viewmodel/viewmodel_property.json" +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_property_string.json b/dev/defs/viewmodel/viewmodel_property_string.json new file mode 100644 index 00000000..ca95b29b --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_property_string.json @@ -0,0 +1,8 @@ +{ + "name": "ViewModelPropertyString", + "key": { + "int": 443, + "string": "viewmodelpropertystring" + }, + "extends": "viewmodel/viewmodel_property.json" +} \ No newline at end of file diff --git a/dev/defs/viewmodel/viewmodel_property_viewmodel.json b/dev/defs/viewmodel/viewmodel_property_viewmodel.json new file mode 100644 index 00000000..10d615a8 --- /dev/null +++ b/dev/defs/viewmodel/viewmodel_property_viewmodel.json @@ -0,0 +1,21 @@ +{ + "name": "ViewModelPropertyViewModel", + "key": { + "int": 436, + "string": "viewmodelpropertyviewmodel" + }, + "extends": "viewmodel/viewmodel_property.json", + "properties": { + "viewModelReferenceId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "0", + "key": { + "int": 565, + "string": "viewmodelreferenceid" + }, + "description": "Identifier used to track the viewmodel this property points to." + } + } +} \ No newline at end of file diff --git a/dev/test.sh b/dev/test.sh index 4e4047bb..213e09a8 100755 --- a/dev/test.sh +++ b/dev/test.sh @@ -75,7 +75,7 @@ RUNTIME=$PWD popd export PREMAKE_PATH="$RUNTIME/dependencies/export-compile-commands":"$RUNTIME/build":"$PREMAKE_PATH" -PREMAKE_COMMANDS="--with_rive_text --with_rive_audio=external --config=$CONFIG" +PREMAKE_COMMANDS="--with_rive_text --with_rive_audio=external --with_rive_layout --config=$CONFIG" out_dir() { echo "out/$CONFIG" diff --git a/dev/test/premake5.lua b/dev/test/premake5.lua index b9079eb4..e6e2cc73 100644 --- a/dev/test/premake5.lua +++ b/dev/test/premake5.lua @@ -6,9 +6,13 @@ defines({ 'WITH_RIVE_TOOLS', 'WITH_RIVE_TEXT', 'WITH_RIVE_AUDIO', + 'WITH_RIVE_AUDIO_TOOLS', + 'WITH_RIVE_LAYOUT', + 'YOGA_EXPORT=', }) dofile(path.join(path.getabsolute('../../'), 'premake5_v2.lua')) +dofile(path.join(path.getabsolute('../../decoders/'), 'premake5_v2.lua')) project('tests') do @@ -18,10 +22,22 @@ do includedirs({ './include', '../../include', + '../../decoders/include', miniaudio, + yoga, }) - links({ 'rive', 'rive_harfbuzz', 'rive_sheenbidi' }) + links({ + 'rive', + 'rive_harfbuzz', + 'rive_sheenbidi', + 'rive_yoga', + 'rive_decoders', + 'libpng', + 'zlib', + 'libjpeg', + 'libwebp', + }) files({ '../../test/**.cpp', -- the tests @@ -39,4 +55,22 @@ do }) forceincludes({ 'rive_harfbuzz_renames.h' }) end + + filter({ 'options:not no-yoga-renames' }) + do + includedirs({ + dependencies, + }) + forceincludes({ 'rive_yoga_renames.h' }) + end + + filter({ 'system:macosx' }) + do + links({ + 'Foundation.framework', + 'ImageIO.framework', + 'CoreGraphics.framework', + 'CoreText.framework', + }) + end end diff --git a/include/rive/animation/animation_reset.hpp b/include/rive/animation/animation_reset.hpp new file mode 100644 index 00000000..2828da32 --- /dev/null +++ b/include/rive/animation/animation_reset.hpp @@ -0,0 +1,32 @@ +#ifndef _RIVE_ANIMATION_RESET_HPP_ +#define _RIVE_ANIMATION_RESET_HPP_ + +#include +#include "rive/artboard.hpp" +#include "rive/animation/animation_reset.hpp" +#include "rive/core/binary_writer.hpp" +#include "rive/core/vector_binary_writer.hpp" +#include "rive/core/binary_data_reader.hpp" + +namespace rive +{ + +class AnimationReset +{ +private: + VectorBinaryWriter m_binaryWriter; + BinaryDataReader m_binaryReader; + std::vector m_WriteBuffer; + +public: + AnimationReset(); + void writeObjectId(uint32_t objectId); + void writeTotalProperties(uint32_t value); + void writePropertyKey(uint32_t value); + void writePropertyValue(float value); + void apply(Artboard* artboard); + void complete(); + void clear(); +}; +} // namespace rive +#endif diff --git a/include/rive/animation/animation_reset_factory.hpp b/include/rive/animation/animation_reset_factory.hpp new file mode 100644 index 00000000..8a4db56e --- /dev/null +++ b/include/rive/animation/animation_reset_factory.hpp @@ -0,0 +1,40 @@ +#ifndef _RIVE_ANIMATION_RESET_FACTORY_HPP_ +#define _RIVE_ANIMATION_RESET_FACTORY_HPP_ + +#include +#include +#include "rive/animation/animation_reset.hpp" +#include "rive/animation/state_instance.hpp" +#include "rive/animation/linear_animation.hpp" +#include "rive/artboard.hpp" + +namespace rive +{ + +class AnimationResetFactory +{ + static std::vector> m_resources; + static std::mutex m_mutex; + +private: + static void fromState(StateInstance* stateInstance, + std::vector& animations); + +public: + static std::unique_ptr getInstance(); + static std::unique_ptr fromStates(StateInstance* stateFrom, + StateInstance* currentState, + ArtboardInstance* artboard); + static std::unique_ptr fromAnimations( + std::vector& animations, + ArtboardInstance* artboard, + bool useFirstAsBaseline); + static void release(std::unique_ptr value); +#ifdef TESTING + // Used in testing to check pooled resources; + static int resourcesCount() { return m_resources.size(); }; + static void releaseResources() { m_resources.clear(); }; +#endif +}; +} // namespace rive +#endif \ No newline at end of file diff --git a/include/rive/animation/animation_state_instance.hpp b/include/rive/animation/animation_state_instance.hpp index 0eb15cc9..7e73343e 100644 --- a/include/rive/animation/animation_state_instance.hpp +++ b/include/rive/animation/animation_state_instance.hpp @@ -20,7 +20,7 @@ class AnimationStateInstance : public StateInstance AnimationStateInstance(const AnimationState* animationState, ArtboardInstance* instance); void advance(float seconds, StateMachineInstance* stateMachineInstance) override; - void apply(float mix) override; + void apply(ArtboardInstance* instance, float mix) override; bool keepGoing() const override; void clearSpilledTime() override; diff --git a/include/rive/animation/arithmetic_operation.hpp b/include/rive/animation/arithmetic_operation.hpp new file mode 100644 index 00000000..1870b8dd --- /dev/null +++ b/include/rive/animation/arithmetic_operation.hpp @@ -0,0 +1,16 @@ +#ifndef _RIVE_ARITHMETIC_OPERATION_HPP_ +#define _RIVE_ARITHMETIC_OPERATION_HPP_ + +namespace rive +{ +enum class ArithmeticOperation : int +{ + add = 0, + subtract = 1, + multiply = 2, + divide = 3, + modulo = 4, +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/artboard_property.hpp b/include/rive/animation/artboard_property.hpp new file mode 100644 index 00000000..0a4d606d --- /dev/null +++ b/include/rive/animation/artboard_property.hpp @@ -0,0 +1,14 @@ +#ifndef _RIVE_ARTBOARD_PROPERTY_HPP_ +#define _RIVE_ARTBOARD_PROPERTY_HPP_ + +namespace rive +{ +enum class ArtboardProperty : int +{ + width = 0, + height = 1, + ratio = 2, +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/blend_state_1d_instance.hpp b/include/rive/animation/blend_state_1d_instance.hpp index d0c81a2d..ed1df09f 100644 --- a/include/rive/animation/blend_state_1d_instance.hpp +++ b/include/rive/animation/blend_state_1d_instance.hpp @@ -4,6 +4,8 @@ #include "rive/animation/blend_state_instance.hpp" #include "rive/animation/blend_state_1d.hpp" #include "rive/animation/blend_animation_1d.hpp" +#include "rive/animation/animation_reset.hpp" +#include "rive/animation/animation_reset_factory.hpp" namespace rive { @@ -12,11 +14,14 @@ class BlendState1DInstance : public BlendStateInstance* m_From = nullptr; BlendStateAnimationInstance* m_To = nullptr; + std::unique_ptr m_AnimationReset; int animationIndex(float value); public: BlendState1DInstance(const BlendState1D* blendState, ArtboardInstance* instance); + ~BlendState1DInstance(); void advance(float seconds, StateMachineInstance* stateMachineInstance) override; + void apply(ArtboardInstance* instance, float mix) override; }; } // namespace rive #endif \ No newline at end of file diff --git a/include/rive/animation/blend_state_instance.hpp b/include/rive/animation/blend_state_instance.hpp index fb52f12c..bf34466f 100644 --- a/include/rive/animation/blend_state_instance.hpp +++ b/include/rive/animation/blend_state_instance.hpp @@ -5,8 +5,11 @@ #include #include "rive/animation/state_instance.hpp" #include "rive/animation/blend_state.hpp" +#include "rive/animation/layer_state_flags.hpp" #include "rive/animation/linear_animation_instance.hpp" #include "rive/animation/state_machine_instance.hpp" +#include "rive/animation/animation_reset.hpp" +#include "rive/animation/animation_reset_factory.hpp" namespace rive { @@ -49,6 +52,15 @@ template class BlendStateInstance : public StateInstance m_AnimationInstances.emplace_back( BlendStateAnimationInstance(static_cast(blendAnimation), instance)); } + if ((static_cast(blendState->flags()) & LayerStateFlags::Reset) == + LayerStateFlags::Reset) + { + auto animations = std::vector(); + for (auto blendAnimation : blendState->animations()) + { + animations.push_back(blendAnimation->animation()); + } + } } bool keepGoing() const override { return m_KeepGoing; } @@ -71,7 +83,7 @@ template class BlendStateInstance : public StateInstance } } - void apply(float mix) override + void apply(ArtboardInstance* instance, float mix) override { for (auto& animation : m_AnimationInstances) { diff --git a/include/rive/animation/keyed_object.hpp b/include/rive/animation/keyed_object.hpp index e11306c7..3aba7b9f 100644 --- a/include/rive/animation/keyed_object.hpp +++ b/include/rive/animation/keyed_object.hpp @@ -19,11 +19,25 @@ class KeyedObject : public KeyedObjectBase void reportKeyedCallbacks(KeyedCallbackReporter* reporter, float secondsFrom, float secondsTo, - int secondsFromExactOffset) const; + bool isAtStartFrame) const; void apply(Artboard* coreContext, float time, float mix); StatusCode import(ImportStack& importStack) override; + const KeyedProperty* getProperty(size_t index) const + { + if (index < m_keyedProperties.size()) + { + return m_keyedProperties[index].get(); + } + else + { + return nullptr; + } + } + + size_t numKeyedProperties() const { return m_keyedProperties.size(); } + private: std::vector> m_keyedProperties; }; diff --git a/include/rive/animation/keyed_property.hpp b/include/rive/animation/keyed_property.hpp index c45f3bd1..d9d04256 100644 --- a/include/rive/animation/keyed_property.hpp +++ b/include/rive/animation/keyed_property.hpp @@ -20,12 +20,20 @@ class KeyedProperty : public KeyedPropertyBase uint32_t objectId, float secondsFrom, float secondsTo, - int secondsFromExactOffset) const; + bool isAtStartFrame) const; /// Apply interpolating key frames. void apply(Core* object, float time, float mix); StatusCode import(ImportStack& importStack) override; + KeyFrame* first() const + { + if (m_keyFrames.size() > 0) + { + return m_keyFrames.front().get(); + } + return nullptr; + } private: int closestFrameIndex(float seconds, int exactOffset = 0) const; diff --git a/include/rive/animation/keyframe_uint.hpp b/include/rive/animation/keyframe_uint.hpp new file mode 100644 index 00000000..2b398de3 --- /dev/null +++ b/include/rive/animation/keyframe_uint.hpp @@ -0,0 +1,19 @@ +#ifndef _RIVE_KEY_FRAME_UINT_HPP_ +#define _RIVE_KEY_FRAME_UINT_HPP_ +#include "rive/generated/animation/keyframe_uint_base.hpp" +#include +namespace rive +{ +class KeyFrameUint : public KeyFrameUintBase +{ +public: + void apply(Core* object, int propertyKey, float mix) override; + void applyInterpolation(Core* object, + int propertyKey, + float seconds, + const KeyFrame* nextFrame, + float mix) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/layer_state_flags.hpp b/include/rive/animation/layer_state_flags.hpp new file mode 100644 index 00000000..c9d61346 --- /dev/null +++ b/include/rive/animation/layer_state_flags.hpp @@ -0,0 +1,26 @@ +#ifndef _RIVE_LAYER_STATE_FLAGS_HPP_ +#define _RIVE_LAYER_STATE_FLAGS_HPP_ + +#include + +namespace rive +{ +enum class LayerStateFlags : unsigned char +{ + None = 0, + + /// Whether the transition is disabled. + Random = 1 << 0, + + /// Whether the blend should include an instance to reset values on apply + Reset = 1 << 1, +}; + +inline constexpr LayerStateFlags operator&(LayerStateFlags lhs, LayerStateFlags rhs) +{ + return static_cast( + static_cast::type>(lhs) & + static_cast::type>(rhs)); +} +} // namespace rive +#endif \ No newline at end of file diff --git a/include/rive/animation/linear_animation.hpp b/include/rive/animation/linear_animation.hpp index 2efcfeb7..01f92053 100644 --- a/include/rive/animation/linear_animation.hpp +++ b/include/rive/animation/linear_animation.hpp @@ -35,21 +35,37 @@ class LinearAnimation : public LinearAnimationBase /// Returns the start time/ end time of the animation in seconds, considering speed float startTime() const; + float startTime(float multiplier) const; float endTime() const; /// Convert a global clock to local seconds (takes into consideration /// work area start/end, speed, looping). float globalToLocalSeconds(float seconds) const; + const KeyedObject* getObject(size_t index) const + { + if (index < m_KeyedObjects.size()) + { + return m_KeyedObjects[index].get(); + } + else + { + return nullptr; + } + } + + size_t numKeyedObjects() const { return m_KeyedObjects.size(); } + #ifdef TESTING - size_t numKeyedObjects() { return m_KeyedObjects.size(); } // Used in testing to check how many animations gets deleted. static int deleteCount; #endif void reportKeyedCallbacks(KeyedCallbackReporter* reporter, float secondsFrom, - float secondsTo) const; + float secondsTo, + float speedDirection, + bool fromPong) const; }; } // namespace rive diff --git a/include/rive/animation/linear_animation_instance.hpp b/include/rive/animation/linear_animation_instance.hpp index a7cb9f6f..d04d5964 100644 --- a/include/rive/animation/linear_animation_instance.hpp +++ b/include/rive/animation/linear_animation_instance.hpp @@ -3,13 +3,15 @@ #include "rive/artboard.hpp" #include "rive/core/field_types/core_callback_type.hpp" +#include "rive/nested_animation.hpp" #include "rive/scene.hpp" namespace rive { class LinearAnimation; +class NestedEventNotifier; -class LinearAnimationInstance : public Scene +class LinearAnimationInstance : public Scene, public NestedEventNotifier { public: LinearAnimationInstance(const LinearAnimation*, ArtboardInstance*, float speedMultiplier = 1.0); @@ -69,6 +71,13 @@ class LinearAnimationInstance : public Scene (directedSpeed() < 0 && m_time > m_animation->startSeconds()); } + bool keepGoing(float speedMultiplier) const + { + return this->loopValue() != static_cast(rive::Loop::oneShot) || + (directedSpeed() * speedMultiplier > 0 && m_time < m_animation->endSeconds()) || + (directedSpeed() * speedMultiplier < 0 && m_time > m_animation->startSeconds()); + } + float totalTime() const { return m_totalTime; } float lastTotalTime() const { return m_lastTotalTime; } float spilledTime() const { return m_spilledTime; } @@ -90,10 +99,12 @@ class LinearAnimationInstance : public Scene bool advanceAndApply(float seconds) override; std::string name() const override; void reset(float speedMultiplier); + void reportEvent(Event* event, float secondsDelay = 0.0f) override; private: const LinearAnimation* m_animation = nullptr; float m_time; + float m_speedDirection; float m_totalTime; float m_lastTotalTime; float m_spilledTime; diff --git a/include/rive/animation/listener_action.hpp b/include/rive/animation/listener_action.hpp index 370a5cc1..691ab0fd 100644 --- a/include/rive/animation/listener_action.hpp +++ b/include/rive/animation/listener_action.hpp @@ -10,7 +10,9 @@ class ListenerAction : public ListenerActionBase { public: StatusCode import(ImportStack& importStack) override; - virtual void perform(StateMachineInstance* stateMachineInstance, Vec2D position) const = 0; + virtual void perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const = 0; }; } // namespace rive diff --git a/include/rive/animation/listener_align_target.hpp b/include/rive/animation/listener_align_target.hpp index dd711082..2a150d6b 100644 --- a/include/rive/animation/listener_align_target.hpp +++ b/include/rive/animation/listener_align_target.hpp @@ -7,7 +7,9 @@ namespace rive class ListenerAlignTarget : public ListenerAlignTargetBase { public: - void perform(StateMachineInstance* stateMachineInstance, Vec2D position) const override; + void perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const override; }; } // namespace rive diff --git a/include/rive/animation/listener_bool_change.hpp b/include/rive/animation/listener_bool_change.hpp index df0b7627..a6f25c24 100644 --- a/include/rive/animation/listener_bool_change.hpp +++ b/include/rive/animation/listener_bool_change.hpp @@ -10,7 +10,9 @@ class ListenerBoolChange : public ListenerBoolChangeBase public: bool validateInputType(const StateMachineInput* input) const override; bool validateNestedInputType(const NestedInput* input) const override; - void perform(StateMachineInstance* stateMachineInstance, Vec2D position) const override; + void perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const override; }; } // namespace rive diff --git a/include/rive/animation/listener_fire_event.hpp b/include/rive/animation/listener_fire_event.hpp index 66c48500..4e4a7d23 100644 --- a/include/rive/animation/listener_fire_event.hpp +++ b/include/rive/animation/listener_fire_event.hpp @@ -7,7 +7,9 @@ namespace rive class ListenerFireEvent : public ListenerFireEventBase { public: - void perform(StateMachineInstance* stateMachineInstance, Vec2D position) const override; + void perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const override; }; } // namespace rive diff --git a/include/rive/animation/listener_number_change.hpp b/include/rive/animation/listener_number_change.hpp index d76cc0e5..613d7d3c 100644 --- a/include/rive/animation/listener_number_change.hpp +++ b/include/rive/animation/listener_number_change.hpp @@ -10,7 +10,9 @@ class ListenerNumberChange : public ListenerNumberChangeBase public: bool validateInputType(const StateMachineInput* input) const override; bool validateNestedInputType(const NestedInput* input) const override; - void perform(StateMachineInstance* stateMachineInstance, Vec2D position) const override; + void perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const override; }; } // namespace rive diff --git a/include/rive/animation/listener_trigger_change.hpp b/include/rive/animation/listener_trigger_change.hpp index 7a9c5954..005f4727 100644 --- a/include/rive/animation/listener_trigger_change.hpp +++ b/include/rive/animation/listener_trigger_change.hpp @@ -10,7 +10,9 @@ class ListenerTriggerChange : public ListenerTriggerChangeBase public: bool validateInputType(const StateMachineInput* input) const override; bool validateNestedInputType(const NestedInput* input) const override; - void perform(StateMachineInstance* stateMachineInstance, Vec2D position) const override; + void perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const override; }; } // namespace rive diff --git a/include/rive/animation/listener_viewmodel_change.hpp b/include/rive/animation/listener_viewmodel_change.hpp new file mode 100644 index 00000000..a9b82a1d --- /dev/null +++ b/include/rive/animation/listener_viewmodel_change.hpp @@ -0,0 +1,21 @@ +#ifndef _RIVE_LISTENER_VIEW_MODEL_CHANGE_HPP_ +#define _RIVE_LISTENER_VIEW_MODEL_CHANGE_HPP_ +#include "rive/generated/animation/listener_viewmodel_change_base.hpp" +#include "rive/data_bind/bindable_property.hpp" +#include +namespace rive +{ +class ListenerViewModelChange : public ListenerViewModelChangeBase +{ +public: + void perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const override; + StatusCode import(ImportStack& importStack) override; + +private: + BindableProperty* m_bindableProperty; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/nested_bool.hpp b/include/rive/animation/nested_bool.hpp index 813ece21..64210696 100644 --- a/include/rive/animation/nested_bool.hpp +++ b/include/rive/animation/nested_bool.hpp @@ -7,10 +7,12 @@ namespace rive class NestedBool : public NestedBoolBase { public: + void nestedValue(bool value) override; + bool nestedValue() const override; + void applyValue() override; protected: - void nestedValueChanged() override; }; } // namespace rive diff --git a/include/rive/animation/nested_input.hpp b/include/rive/animation/nested_input.hpp index 16d5bf99..fe75467c 100644 --- a/include/rive/animation/nested_input.hpp +++ b/include/rive/animation/nested_input.hpp @@ -1,6 +1,7 @@ #ifndef _RIVE_NESTED_INPUT_HPP_ #define _RIVE_NESTED_INPUT_HPP_ #include "rive/animation/nested_state_machine.hpp" +#include "rive/animation/state_machine_input_instance.hpp" #include "rive/generated/animation/nested_input_base.hpp" #include namespace rive @@ -21,8 +22,7 @@ class NestedInput : public NestedInputBase virtual void applyValue() {} -protected: - SMIInput* input() + SMIInput* input() const { auto parent = this->parent(); if (parent != nullptr && parent->is()) @@ -34,6 +34,16 @@ class NestedInput : public NestedInputBase } return nullptr; } + + const std::string name() const + { + auto smi = input(); + if (smi != nullptr) + { + return smi->name(); + } + return std::string(); + } }; } // namespace rive diff --git a/include/rive/animation/nested_linear_animation.hpp b/include/rive/animation/nested_linear_animation.hpp index 7de512c8..6d307270 100644 --- a/include/rive/animation/nested_linear_animation.hpp +++ b/include/rive/animation/nested_linear_animation.hpp @@ -15,6 +15,7 @@ class NestedLinearAnimation : public NestedLinearAnimationBase ~NestedLinearAnimation() override; void initializeAnimation(ArtboardInstance*) override; + LinearAnimationInstance* animationInstance() { return m_AnimationInstance.get(); } }; } // namespace rive diff --git a/include/rive/animation/nested_number.hpp b/include/rive/animation/nested_number.hpp index b21dfc3c..f9651260 100644 --- a/include/rive/animation/nested_number.hpp +++ b/include/rive/animation/nested_number.hpp @@ -7,10 +7,11 @@ namespace rive class NestedNumber : public NestedNumberBase { public: + void nestedValue(float value) override; + float nestedValue() const override; void applyValue() override; protected: - void nestedValueChanged() override; }; } // namespace rive diff --git a/include/rive/animation/nested_remap_animation.hpp b/include/rive/animation/nested_remap_animation.hpp index c83002c9..3b304fd9 100644 --- a/include/rive/animation/nested_remap_animation.hpp +++ b/include/rive/animation/nested_remap_animation.hpp @@ -8,7 +8,7 @@ class NestedRemapAnimation : public NestedRemapAnimationBase { public: void timeChanged() override; - void advance(float elapsedSeconds) override; + bool advance(float elapsedSeconds) override; void initializeAnimation(ArtboardInstance*) override; }; } // namespace rive diff --git a/include/rive/animation/nested_simple_animation.hpp b/include/rive/animation/nested_simple_animation.hpp index 41bc958c..ce04aa44 100644 --- a/include/rive/animation/nested_simple_animation.hpp +++ b/include/rive/animation/nested_simple_animation.hpp @@ -7,7 +7,7 @@ namespace rive class NestedSimpleAnimation : public NestedSimpleAnimationBase { public: - void advance(float elapsedSeconds) override; + bool advance(float elapsedSeconds) override; }; } // namespace rive diff --git a/include/rive/animation/nested_state_machine.hpp b/include/rive/animation/nested_state_machine.hpp index fe0cded9..f3e557c1 100644 --- a/include/rive/animation/nested_state_machine.hpp +++ b/include/rive/animation/nested_state_machine.hpp @@ -20,7 +20,7 @@ class NestedStateMachine : public NestedStateMachineBase public: NestedStateMachine(); ~NestedStateMachine() override; - void advance(float elapsedSeconds) override; + bool advance(float elapsedSeconds) override; void initializeAnimation(ArtboardInstance*) override; StateMachineInstance* stateMachineInstance(); @@ -28,8 +28,16 @@ class NestedStateMachine : public NestedStateMachineBase HitResult pointerDown(Vec2D position); HitResult pointerUp(Vec2D position); HitResult pointerExit(Vec2D position); +#ifdef WITH_RIVE_TOOLS + bool hitTest(Vec2D position) const; +#endif void addNestedInput(NestedInput* input); + size_t inputCount() { return m_nestedInputs.size(); } + NestedInput* input(size_t index); + NestedInput* input(std::string name); + void dataContextFromInstance(ViewModelInstance* viewModelInstance); + void dataContext(DataContext* dataContext); }; } // namespace rive diff --git a/include/rive/animation/state_instance.hpp b/include/rive/animation/state_instance.hpp index 401b9f06..1dffd9da 100644 --- a/include/rive/animation/state_instance.hpp +++ b/include/rive/animation/state_instance.hpp @@ -22,7 +22,7 @@ class StateInstance StateInstance(const LayerState* layerState); virtual ~StateInstance(); virtual void advance(float seconds, StateMachineInstance* stateMachineInstance) = 0; - virtual void apply(float mix) = 0; + virtual void apply(ArtboardInstance* instance, float mix) = 0; /// Returns true when the State Machine needs to keep advancing this /// state. diff --git a/include/rive/animation/state_machine.hpp b/include/rive/animation/state_machine.hpp index 9899893b..258fe612 100644 --- a/include/rive/animation/state_machine.hpp +++ b/include/rive/animation/state_machine.hpp @@ -10,6 +10,7 @@ class StateMachineLayer; class StateMachineInput; class StateMachineListener; class StateMachineImporter; +class DataBind; class StateMachine : public StateMachineBase { friend class StateMachineImporter; @@ -18,10 +19,12 @@ class StateMachine : public StateMachineBase std::vector> m_Layers; std::vector> m_Inputs; std::vector> m_Listeners; + std::vector> m_dataBinds; void addLayer(std::unique_ptr); void addInput(std::unique_ptr); void addListener(std::unique_ptr); + void addDataBind(std::unique_ptr); public: StateMachine(); @@ -32,11 +35,13 @@ class StateMachine : public StateMachineBase size_t layerCount() const { return m_Layers.size(); } size_t inputCount() const { return m_Inputs.size(); } size_t listenerCount() const { return m_Listeners.size(); } + size_t dataBindCount() const { return m_dataBinds.size(); } const StateMachineInput* input(std::string name) const; const StateMachineInput* input(size_t index) const; const StateMachineLayer* layer(std::string name) const; const StateMachineLayer* layer(size_t index) const; + const DataBind* dataBind(size_t index) const; const StateMachineListener* listener(size_t index) const; StatusCode onAddedDirty(CoreContext* context) override; diff --git a/include/rive/animation/state_machine_input_instance.hpp b/include/rive/animation/state_machine_input_instance.hpp index e5f732c1..8c82a71b 100644 --- a/include/rive/animation/state_machine_input_instance.hpp +++ b/include/rive/animation/state_machine_input_instance.hpp @@ -20,9 +20,6 @@ class SMIInput friend class StateMachineLayerInstance; private: - StateMachineInstance* m_MachineInstance; - const StateMachineInput* m_Input; - virtual void advanced() {} protected: @@ -32,10 +29,17 @@ class SMIInput public: virtual ~SMIInput() {} - const StateMachineInput* input() const { return m_Input; } + const StateMachineInput* input() const { return m_input; } const std::string& name() const; uint16_t inputCoreType() const; + +private: + StateMachineInstance* m_machineInstance; + const StateMachineInput* m_input; +#ifdef WITH_RIVE_TOOLS + uint64_t m_index = 0; +#endif }; class SMIBool : public SMIInput @@ -72,16 +76,16 @@ class SMITrigger : public SMIInput friend class TransitionTriggerCondition; private: - bool m_Fired = false; + bool m_fired = false; SMITrigger(const StateMachineTrigger* input, StateMachineInstance* machineInstance); - void advanced() override { m_Fired = false; } + void advanced() override { m_fired = false; } public: void fire(); #ifdef TESTING - bool didFire() { return m_Fired; } + bool didFire() { return m_fired; } #endif }; } // namespace rive diff --git a/include/rive/animation/state_machine_instance.hpp b/include/rive/animation/state_machine_instance.hpp index 67369e69..5921adf6 100644 --- a/include/rive/animation/state_machine_instance.hpp +++ b/include/rive/animation/state_machine_instance.hpp @@ -4,10 +4,14 @@ #include #include #include +#include #include "rive/animation/linear_animation_instance.hpp" +#include "rive/animation/state_instance.hpp" +#include "rive/animation/state_transition.hpp" #include "rive/core/field_types/core_callback_type.hpp" #include "rive/hit_result.hpp" #include "rive/listener_type.hpp" +#include "rive/nested_animation.hpp" #include "rive/scene.hpp" namespace rive @@ -22,27 +26,28 @@ class SMITrigger; class Shape; class StateMachineLayerInstance; class HitComponent; +class HitShape; +class ListenerGroup; class NestedArtboard; +class NestedEventListener; +class NestedEventNotifier; class Event; class KeyedProperty; +class EventReport; +class DataBind; +class BindableProperty; -class EventReport -{ -public: - EventReport(Event* event, float secondsDelay) : m_event(event), m_secondsDelay(secondsDelay) {} - Event* event() const { return m_event; } - float secondsDelay() const { return m_secondsDelay; } - -private: - Event* m_event; - float m_secondsDelay; -}; +#ifdef WITH_RIVE_TOOLS +class StateMachineInstance; +typedef void (*InputChanged)(StateMachineInstance*, uint64_t); +#endif -class StateMachineInstance : public Scene +class StateMachineInstance : public Scene, public NestedEventNotifier, public NestedEventListener { friend class SMIInput; friend class KeyedProperty; friend class HitComponent; + friend class StateMachineLayerInstance; private: /// Provide a hitListener if you want to process a down or an up for the pointer position @@ -51,8 +56,12 @@ class StateMachineInstance : public Scene template InstType* getNamedInput(const std::string& name) const; - void notifyEventListeners(std::vector events, NestedArtboard* source); + void notifyEventListeners(const std::vector& events, NestedArtboard* source); void sortHitComponents(); + double randomValue(); + StateTransition* findRandomTransition(StateInstance* stateFromInstance, bool ignoreTriggers); + StateTransition* findAllowedTransition(StateInstance* stateFromInstance, bool ignoreTriggers); + DataContext* m_DataContext; public: StateMachineInstance(const StateMachine* machine, ArtboardInstance* instance); @@ -75,6 +84,8 @@ class StateMachineInstance : public Scene SMIBool* getBool(const std::string& name) const override; SMINumber* getNumber(const std::string& name) const override; SMITrigger* getTrigger(const std::string& name) const override; + void dataContextFromInstance(ViewModelInstance* viewModelInstance) override; + void dataContext(DataContext* dataContext); size_t currentAnimationCount() const; const LinearAnimationInstance* currentAnimationByIndex(size_t index) const; @@ -94,6 +105,9 @@ class StateMachineInstance : public Scene HitResult pointerDown(Vec2D position) override; HitResult pointerUp(Vec2D position) override; HitResult pointerExit(Vec2D position) override; +#ifdef WITH_RIVE_TOOLS + bool hitTest(Vec2D position) const; +#endif float durationSeconds() const override { return -1; } Loop loop() const override { return Loop::oneShot; } @@ -101,7 +115,7 @@ class StateMachineInstance : public Scene /// Allow anything referencing a concrete StateMachineInstace access to /// the backing artboard (explicitly not allowed on Scenes). - Artboard* artboard() { return m_artboardInstance; } + Artboard* artboard() const { return m_artboardInstance; } void setParentStateMachineInstance(StateMachineInstance* instance) { @@ -111,6 +125,7 @@ class StateMachineInstance : public Scene void setParentNestedArtboard(NestedArtboard* artboard) { m_parentNestedArtboard = artboard; } NestedArtboard* parentNestedArtboard() { return m_parentNestedArtboard; } + void notify(const std::vector& events, NestedArtboard* context) override; /// Tracks an event that reported, will be cleared at the end of the next advance. void reportEvent(Event* event, float secondsDelay = 0.0f) override; @@ -120,6 +135,22 @@ class StateMachineInstance : public Scene /// Gets a reported event at an index < reportedEventCount(). const EventReport reportedEventAt(std::size_t index) const; + bool playsAudio() override { return true; } + BindableProperty* bindablePropertyInstance(BindableProperty* bindableProperty) const; + DataBind* bindableDataBind(BindableProperty* bindableProperty); +#ifdef TESTING + size_t hitComponentsCount() { return m_hitComponents.size(); }; + HitComponent* hitComponent(size_t index) + { + if (index < m_hitComponents.size()) + { + return m_hitComponents[index].get(); + } + return nullptr; + } + const LayerState* layerState(size_t index); +#endif + void updateDataBinds(); private: std::vector m_reportedEvents; @@ -129,8 +160,41 @@ class StateMachineInstance : public Scene size_t m_layerCount; StateMachineLayerInstance* m_layers; std::vector> m_hitComponents; + std::vector> m_listenerGroups; StateMachineInstance* m_parentStateMachineInstance = nullptr; NestedArtboard* m_parentNestedArtboard = nullptr; + std::vector m_dataBinds; + std::unordered_map m_bindablePropertyInstances; + std::unordered_map m_bindableDataBinds; + +#ifdef WITH_RIVE_TOOLS +public: + void onInputChanged(InputChanged callback) { m_inputChangedCallback = callback; } + InputChanged m_inputChangedCallback = nullptr; +#endif +}; + +class HitComponent +{ +public: + Component* component() const { return m_component; } + HitComponent(Component* component, StateMachineInstance* stateMachineInstance) : + m_component(component), m_stateMachineInstance(stateMachineInstance) + {} + virtual ~HitComponent() {} + virtual HitResult processEvent(Vec2D position, ListenerType hitType, bool canHit) = 0; + virtual void prepareEvent(Vec2D position, ListenerType hitType) = 0; +#ifdef WITH_RIVE_TOOLS + virtual bool hitTest(Vec2D position) const = 0; +#endif +#ifdef TESTING + int earlyOutCount = 0; +#endif + +protected: + Component* m_component; + StateMachineInstance* m_stateMachineInstance; }; + } // namespace rive #endif diff --git a/include/rive/animation/state_machine_listener.hpp b/include/rive/animation/state_machine_listener.hpp index 88be3c07..2867188b 100644 --- a/include/rive/animation/state_machine_listener.hpp +++ b/include/rive/animation/state_machine_listener.hpp @@ -14,24 +14,23 @@ class StateMachineListener : public StateMachineListenerBase { friend class StateMachineListenerImporter; -private: - std::vector m_HitShapesIds; - std::vector> m_Actions; - void addAction(std::unique_ptr); - public: StateMachineListener(); ~StateMachineListener() override; ListenerType listenerType() const { return (ListenerType)listenerTypeValue(); } - size_t actionCount() const { return m_Actions.size(); } + size_t actionCount() const { return m_actions.size(); } const ListenerAction* action(size_t index) const; StatusCode import(ImportStack& importStack) override; - StatusCode onAddedClean(CoreContext* context) override; - const std::vector& hitShapeIds() const { return m_HitShapesIds; } - void performChanges(StateMachineInstance* stateMachineInstance, Vec2D position) const; + void performChanges(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const; + +private: + void addAction(std::unique_ptr); + std::vector> m_actions; }; } // namespace rive diff --git a/include/rive/animation/state_transition.hpp b/include/rive/animation/state_transition.hpp index 412ac065..c021c2c5 100644 --- a/include/rive/animation/state_transition.hpp +++ b/include/rive/animation/state_transition.hpp @@ -35,6 +35,7 @@ class StateTransition : public StateTransitionBase return static_cast(flags()); } LayerState* m_StateTo = nullptr; + uint32_t m_EvaluatedRandomWeight = 1; CubicInterpolator* m_Interpolator = nullptr; std::vector m_Conditions; @@ -45,6 +46,9 @@ class StateTransition : public StateTransitionBase const LayerState* stateTo() const { return m_StateTo; } inline CubicInterpolator* interpolator() const { return m_Interpolator; } + inline uint32_t evaluatedRandomWeight() const { return m_EvaluatedRandomWeight; } + void evaluatedRandomWeight(uint32_t value) { m_EvaluatedRandomWeight = value; } + StatusCode onAddedDirty(CoreContext* context) override; StatusCode onAddedClean(CoreContext* context) override; diff --git a/include/rive/animation/system_state_instance.hpp b/include/rive/animation/system_state_instance.hpp index b439da8f..49f64b0e 100644 --- a/include/rive/animation/system_state_instance.hpp +++ b/include/rive/animation/system_state_instance.hpp @@ -17,7 +17,7 @@ class SystemStateInstance : public StateInstance SystemStateInstance(const LayerState* layerState, ArtboardInstance* instance); void advance(float seconds, StateMachineInstance* stateMachineInstance) override; - void apply(float mix) override; + void apply(ArtboardInstance* artboard, float mix) override; bool keepGoing() const override; }; diff --git a/include/rive/animation/transition_artboard_condition.hpp b/include/rive/animation/transition_artboard_condition.hpp new file mode 100644 index 00000000..7892537e --- /dev/null +++ b/include/rive/animation/transition_artboard_condition.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_TRANSITION_ARTBOARD_CONDITION_HPP_ +#define _RIVE_TRANSITION_ARTBOARD_CONDITION_HPP_ +#include "rive/generated/animation/transition_artboard_condition_base.hpp" +#include +namespace rive +{ +class TransitionArtboardCondition : public TransitionArtboardConditionBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/transition_bool_condition.hpp b/include/rive/animation/transition_bool_condition.hpp index 3a41a166..fa5a03e5 100644 --- a/include/rive/animation/transition_bool_condition.hpp +++ b/include/rive/animation/transition_bool_condition.hpp @@ -7,7 +7,7 @@ namespace rive class TransitionBoolCondition : public TransitionBoolConditionBase { public: - bool evaluate(const SMIInput* inputInstance) const override; + bool evaluate(const StateMachineInstance* stateMachineInstance) const override; protected: bool validateInputType(const StateMachineInput* input) const override; diff --git a/include/rive/animation/transition_comparator.hpp b/include/rive/animation/transition_comparator.hpp new file mode 100644 index 00000000..38aab796 --- /dev/null +++ b/include/rive/animation/transition_comparator.hpp @@ -0,0 +1,28 @@ +#ifndef _RIVE_TRANSITION_COMPARATOR_HPP_ +#define _RIVE_TRANSITION_COMPARATOR_HPP_ +#include "rive/generated/animation/transition_comparator_base.hpp" +#include "rive/animation/transition_condition_op.hpp" +#include "rive/viewmodel/viewmodel_instance.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" +#include +namespace rive +{ +class StateMachineInstance; +class TransitionComparator : public TransitionComparatorBase +{ +public: + StatusCode import(ImportStack& importStack) override; + virtual bool compare(TransitionComparator* comparand, + TransitionConditionOp operation, + const StateMachineInstance* stateMachineInstance); + +protected: + bool compareNumbers(float left, float right, TransitionConditionOp op); + bool compareBooleans(bool left, bool right, TransitionConditionOp op); + bool compareEnums(uint16_t left, uint16_t right, TransitionConditionOp op); + bool compareColors(int left, int right, TransitionConditionOp op); + bool compareStrings(std::string left, std::string right, TransitionConditionOp op); +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/transition_condition.hpp b/include/rive/animation/transition_condition.hpp index 51980138..9fc8db6a 100644 --- a/include/rive/animation/transition_condition.hpp +++ b/include/rive/animation/transition_condition.hpp @@ -1,6 +1,7 @@ #ifndef _RIVE_TRANSITION_CONDITION_HPP_ #define _RIVE_TRANSITION_CONDITION_HPP_ #include "rive/generated/animation/transition_condition_base.hpp" +#include "rive/animation/state_machine_instance.hpp" namespace rive { @@ -15,7 +16,7 @@ class TransitionCondition : public TransitionConditionBase StatusCode import(ImportStack& importStack) override; - virtual bool evaluate(const SMIInput* inputInstance) const { return true; } + virtual bool evaluate(const StateMachineInstance* stateMachineInstance) const { return true; } protected: virtual bool validateInputType(const StateMachineInput* input) const { return true; } diff --git a/include/rive/animation/transition_input_condition.hpp b/include/rive/animation/transition_input_condition.hpp new file mode 100644 index 00000000..90ab05a7 --- /dev/null +++ b/include/rive/animation/transition_input_condition.hpp @@ -0,0 +1,14 @@ +#ifndef _RIVE_TRANSITION_INPUT_CONDITION_HPP_ +#define _RIVE_TRANSITION_INPUT_CONDITION_HPP_ +#include "rive/generated/animation/transition_input_condition_base.hpp" +#include +namespace rive +{ +class TransitionInputCondition : public TransitionInputConditionBase +{ +public: + StatusCode import(ImportStack& importStack) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/transition_number_condition.hpp b/include/rive/animation/transition_number_condition.hpp index e8c88d93..d7940970 100644 --- a/include/rive/animation/transition_number_condition.hpp +++ b/include/rive/animation/transition_number_condition.hpp @@ -10,7 +10,7 @@ class TransitionNumberCondition : public TransitionNumberConditionBase bool validateInputType(const StateMachineInput* input) const override; public: - bool evaluate(const SMIInput* inputInstance) const override; + bool evaluate(const StateMachineInstance* stateMachineInstance) const override; }; } // namespace rive diff --git a/include/rive/animation/transition_property_artboard_comparator.hpp b/include/rive/animation/transition_property_artboard_comparator.hpp new file mode 100644 index 00000000..184b6490 --- /dev/null +++ b/include/rive/animation/transition_property_artboard_comparator.hpp @@ -0,0 +1,20 @@ +#ifndef _RIVE_TRANSITION_PROPERTY_ARTBOARD_COMPARATOR_HPP_ +#define _RIVE_TRANSITION_PROPERTY_ARTBOARD_COMPARATOR_HPP_ +#include "rive/generated/animation/transition_property_artboard_comparator_base.hpp" +#include +namespace rive +{ +class Artboard; +class TransitionPropertyArtboardComparator : public TransitionPropertyArtboardComparatorBase +{ +public: + bool compare(TransitionComparator* comparand, + TransitionConditionOp operation, + const StateMachineInstance* stateMachineInstance) override; + +private: + float propertyValue(const StateMachineInstance* stateMachineInstance); +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/transition_property_comparator.hpp b/include/rive/animation/transition_property_comparator.hpp new file mode 100644 index 00000000..e2d9914a --- /dev/null +++ b/include/rive/animation/transition_property_comparator.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_TRANSITION_PROPERTY_COMPARATOR_HPP_ +#define _RIVE_TRANSITION_PROPERTY_COMPARATOR_HPP_ +#include "rive/generated/animation/transition_property_comparator_base.hpp" +#include +namespace rive +{ +class TransitionPropertyComparator : public TransitionPropertyComparatorBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/transition_property_viewmodel_comparator.hpp b/include/rive/animation/transition_property_viewmodel_comparator.hpp new file mode 100644 index 00000000..6c5bd87c --- /dev/null +++ b/include/rive/animation/transition_property_viewmodel_comparator.hpp @@ -0,0 +1,33 @@ +#ifndef _RIVE_TRANSITION_PROPERTY_VIEW_MODEL_COMPARATOR_HPP_ +#define _RIVE_TRANSITION_PROPERTY_VIEW_MODEL_COMPARATOR_HPP_ +#include "rive/generated/animation/transition_property_viewmodel_comparator_base.hpp" +#include "rive/data_bind/bindable_property.hpp" +#include "rive/animation/state_machine_instance.hpp" +#include +namespace rive +{ +class TransitionPropertyViewModelComparator : public TransitionPropertyViewModelComparatorBase +{ +public: + StatusCode import(ImportStack& importStack) override; + bool compare(TransitionComparator* comparand, + TransitionConditionOp operation, + const StateMachineInstance* stateMachineInstance) override; + template + U value(const StateMachineInstance* stateMachineInstance) + { + if (m_bindableProperty->is()) + { + auto bindableInstance = + stateMachineInstance->bindablePropertyInstance(m_bindableProperty); + return bindableInstance->as()->propertyValue(); + } + return (new T())->propertyValue(); + }; + +protected: + BindableProperty* m_bindableProperty; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/transition_trigger_condition.hpp b/include/rive/animation/transition_trigger_condition.hpp index cb535668..7ad999fd 100644 --- a/include/rive/animation/transition_trigger_condition.hpp +++ b/include/rive/animation/transition_trigger_condition.hpp @@ -7,7 +7,7 @@ namespace rive class TransitionTriggerCondition : public TransitionTriggerConditionBase { public: - bool evaluate(const SMIInput* inputInstance) const override; + bool evaluate(const StateMachineInstance* stateMachineInstance) const override; protected: bool validateInputType(const StateMachineInput* input) const override; diff --git a/include/rive/animation/transition_value_boolean_comparator.hpp b/include/rive/animation/transition_value_boolean_comparator.hpp new file mode 100644 index 00000000..fc8b51ce --- /dev/null +++ b/include/rive/animation/transition_value_boolean_comparator.hpp @@ -0,0 +1,16 @@ +#ifndef _RIVE_TRANSITION_VALUE_BOOLEAN_COMPARATOR_HPP_ +#define _RIVE_TRANSITION_VALUE_BOOLEAN_COMPARATOR_HPP_ +#include "rive/generated/animation/transition_value_boolean_comparator_base.hpp" +#include +namespace rive +{ +class TransitionValueBooleanComparator : public TransitionValueBooleanComparatorBase +{ +public: + bool compare(TransitionComparator* comparand, + TransitionConditionOp operation, + const StateMachineInstance* stateMachineInstance) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/transition_value_color_comparator.hpp b/include/rive/animation/transition_value_color_comparator.hpp new file mode 100644 index 00000000..c75c0627 --- /dev/null +++ b/include/rive/animation/transition_value_color_comparator.hpp @@ -0,0 +1,16 @@ +#ifndef _RIVE_TRANSITION_VALUE_COLOR_COMPARATOR_HPP_ +#define _RIVE_TRANSITION_VALUE_COLOR_COMPARATOR_HPP_ +#include "rive/generated/animation/transition_value_color_comparator_base.hpp" +#include +namespace rive +{ +class TransitionValueColorComparator : public TransitionValueColorComparatorBase +{ +public: + bool compare(TransitionComparator* comparand, + TransitionConditionOp operation, + const StateMachineInstance* stateMachineInstance) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/transition_value_comparator.hpp b/include/rive/animation/transition_value_comparator.hpp new file mode 100644 index 00000000..4ebba61b --- /dev/null +++ b/include/rive/animation/transition_value_comparator.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_TRANSITION_VALUE_COMPARATOR_HPP_ +#define _RIVE_TRANSITION_VALUE_COMPARATOR_HPP_ +#include "rive/generated/animation/transition_value_comparator_base.hpp" +#include +namespace rive +{ +class TransitionValueComparator : public TransitionValueComparatorBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/transition_value_enum_comparator.hpp b/include/rive/animation/transition_value_enum_comparator.hpp new file mode 100644 index 00000000..62bd13d3 --- /dev/null +++ b/include/rive/animation/transition_value_enum_comparator.hpp @@ -0,0 +1,11 @@ +#ifndef _RIVE_TRANSITION_VALUE_ENUM_COMPARATOR_HPP_ +#define _RIVE_TRANSITION_VALUE_ENUM_COMPARATOR_HPP_ +#include "rive/generated/animation/transition_value_enum_comparator_base.hpp" +#include +namespace rive +{ +class TransitionValueEnumComparator : public TransitionValueEnumComparatorBase +{}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/transition_value_number_comparator.hpp b/include/rive/animation/transition_value_number_comparator.hpp new file mode 100644 index 00000000..daf644ce --- /dev/null +++ b/include/rive/animation/transition_value_number_comparator.hpp @@ -0,0 +1,16 @@ +#ifndef _RIVE_TRANSITION_VALUE_NUMBER_COMPARATOR_HPP_ +#define _RIVE_TRANSITION_VALUE_NUMBER_COMPARATOR_HPP_ +#include "rive/generated/animation/transition_value_number_comparator_base.hpp" +#include +namespace rive +{ +class TransitionValueNumberComparator : public TransitionValueNumberComparatorBase +{ +public: + bool compare(TransitionComparator* comparand, + TransitionConditionOp operation, + const StateMachineInstance* stateMachineInstance) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/transition_value_string_comparator.hpp b/include/rive/animation/transition_value_string_comparator.hpp new file mode 100644 index 00000000..c634db8f --- /dev/null +++ b/include/rive/animation/transition_value_string_comparator.hpp @@ -0,0 +1,16 @@ +#ifndef _RIVE_TRANSITION_VALUE_STRING_COMPARATOR_HPP_ +#define _RIVE_TRANSITION_VALUE_STRING_COMPARATOR_HPP_ +#include "rive/generated/animation/transition_value_string_comparator_base.hpp" +#include +namespace rive +{ +class TransitionValueStringComparator : public TransitionValueStringComparatorBase +{ +public: + bool compare(TransitionComparator* comparand, + TransitionConditionOp operation, + const StateMachineInstance* stateMachineInstance) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/animation/transition_viewmodel_condition.hpp b/include/rive/animation/transition_viewmodel_condition.hpp new file mode 100644 index 00000000..508807d6 --- /dev/null +++ b/include/rive/animation/transition_viewmodel_condition.hpp @@ -0,0 +1,35 @@ +#ifndef _RIVE_TRANSITION_VIEW_MODEL_CONDITION_HPP_ +#define _RIVE_TRANSITION_VIEW_MODEL_CONDITION_HPP_ +#include "rive/generated/animation/transition_viewmodel_condition_base.hpp" +#include "rive/animation/transition_comparator.hpp" +#include "rive/animation/transition_condition_op.hpp" +#include "rive/animation/state_machine_instance.hpp" +#include +namespace rive +{ +class TransitionViewModelCondition : public TransitionViewModelConditionBase +{ +protected: + TransitionComparator* m_leftComparator; + TransitionComparator* m_rightComparator; + +public: + bool evaluate(const StateMachineInstance* stateMachineInstance) const override; + TransitionComparator* leftComparator() const { return m_leftComparator; }; + TransitionComparator* rightComparator() const { return m_rightComparator; }; + void comparator(TransitionComparator* value) + { + if (m_leftComparator == nullptr) + { + m_leftComparator = value; + } + else + { + m_rightComparator = value; + } + }; + TransitionConditionOp op() const { return (TransitionConditionOp)opValue(); } +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp index 8338a6d9..b054943f 100644 --- a/include/rive/artboard.hpp +++ b/include/rive/artboard.hpp @@ -4,16 +4,22 @@ #include "rive/animation/linear_animation.hpp" #include "rive/animation/state_machine.hpp" #include "rive/core_context.hpp" +#include "rive/data_bind/data_bind.hpp" +#include "rive/data_bind/data_context.hpp" +#include "rive/data_bind/data_bind_context.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" +#include "rive/viewmodel/viewmodel_instance_viewmodel.hpp" #include "rive/generated/artboard_base.hpp" #include "rive/hit_info.hpp" #include "rive/math/aabb.hpp" #include "rive/renderer.hpp" -#include "rive/shapes/shape_paint_container.hpp" #include "rive/text/text_value_run.hpp" #include "rive/event.hpp" #include "rive/audio/audio_engine.hpp" +#include "rive/math/raw_path.hpp" #include +#include #include namespace rive @@ -32,8 +38,16 @@ class StateMachineInstance; class Joystick; class TextValueRun; class Event; +class SMIBool; +class SMIInput; +class SMINumber; +class SMITrigger; -class Artboard : public ArtboardBase, public CoreContext, public ShapePaintContainer +#ifdef WITH_RIVE_TOOLS +typedef void (*ArtboardCallback)(Artboard*); +#endif + +class Artboard : public ArtboardBase, public CoreContext { friend class File; friend class ArtboardImporter; @@ -48,16 +62,25 @@ class Artboard : public ArtboardBase, public CoreContext, public ShapePaintConta std::vector m_DrawTargets; std::vector m_NestedArtboards; std::vector m_Joysticks; + std::vector m_DataBinds; + std::vector m_AllDataBinds; + DataContext* m_DataContext = nullptr; bool m_JoysticksApplyBeforeUpdate = true; bool m_HasChangedDrawOrderInLastUpdate = false; unsigned int m_DirtDepth = 0; - rcp m_BackgroundPath; - rcp m_ClipPath; + RawPath m_backgroundRawPath; Factory* m_Factory = nullptr; Drawable* m_FirstDrawable = nullptr; bool m_IsInstance = false; bool m_FrameOrigin = true; + std::unordered_set m_dirtyLayout; + float m_originalWidth = 0; + float m_originalHeight = 0; + bool m_updatesOwnLayout = true; + Artboard* parentArtboard() const; + NestedArtboard* m_host = nullptr; + bool sharesLayoutWithHost() const; #ifdef EXTERNAL_RIVE_AUDIO_ENGINE rcp m_audioEngine; @@ -65,9 +88,15 @@ class Artboard : public ArtboardBase, public CoreContext, public ShapePaintConta void sortDependencies(); void sortDrawOrder(); + void updateDataBinds(); + void updateRenderPath() override; + void update(ComponentDirt value) override; - Artboard* getArtboard() override { return this; } +public: + void host(NestedArtboard* nestedArtboard); + NestedArtboard* host() const; +private: #ifdef TESTING public: Artboard(Factory* factory) : m_Factory(factory) {} @@ -77,7 +106,7 @@ class Artboard : public ArtboardBase, public CoreContext, public ShapePaintConta void addStateMachine(StateMachine* object); public: - Artboard() {} + Artboard(); ~Artboard() override; StatusCode initialize(); @@ -91,16 +120,28 @@ class Artboard : public ArtboardBase, public CoreContext, public ShapePaintConta // EXPERIMENTAL -- for internal testing only for now. // DO NOT RELY ON THIS as it may change/disappear in the future. - Core* hitTest(HitInfo*, const Mat2D* = nullptr); + Core* hitTest(HitInfo*, const Mat2D&) override; void onComponentDirty(Component* component); /// Update components that depend on each other in DAG order. bool updateComponents(); - void update(ComponentDirt value) override; void onDirty(ComponentDirt dirt) override; - bool advance(double elapsedSeconds); + // Artboards don't update their world transforms in the same way + // as other TransformComponents so we override this. + // This is because LayoutComponent extends Drawable, but + // Artboard is a special type of LayoutComponent + void updateWorldTransform() override {} + + void markLayoutDirty(LayoutComponent* layoutComponent); + + void* takeLayoutNode(); + bool syncStyleChanges(); + bool canHaveOverrides() override { return true; } + + bool advance(double elapsedSeconds, bool nested = true); + bool advanceInternal(double elapsedSeconds, bool isRoot, bool nested = true); bool hasChangedDrawOrderInLastUpdate() { return m_HasChangedDrawOrderInLastUpdate; }; Drawable* firstDrawable() { return m_FirstDrawable; }; @@ -110,23 +151,46 @@ class Artboard : public ArtboardBase, public CoreContext, public ShapePaintConta kHideBG, kHideFG, }; - void draw(Renderer* renderer, DrawOption = DrawOption::kNormal); + void draw(Renderer* renderer, DrawOption option); + void draw(Renderer* renderer) override; void addToRenderPath(RenderPath* path, const Mat2D& transform); #ifdef TESTING - RenderPath* clipPath() const { return m_ClipPath.get(); } - RenderPath* backgroundPath() const { return m_BackgroundPath.get(); } + RenderPath* clipPath() const { return m_clipPath.get(); } + RenderPath* backgroundPath() const { return m_backgroundPath.get(); } #endif const std::vector& objects() const { return m_Objects; } const std::vector nestedArtboards() const { return m_NestedArtboards; } - + const std::vector dataBinds() const { return m_DataBinds; } + DataContext* dataContext() { return m_DataContext; } + NestedArtboard* nestedArtboard(const std::string& name) const; + NestedArtboard* nestedArtboardAtPath(const std::string& path) const; + + float originalWidth() const { return m_originalWidth; } + float originalHeight() const { return m_originalHeight; } + float layoutWidth() const; + float layoutHeight() const; + float layoutX() const; + float layoutY() const; AABB bounds() const; + Vec2D origin() const; // Can we hide these from the public? (they use playable) bool isTranslucent() const; bool isTranslucent(const LinearAnimation*) const; bool isTranslucent(const LinearAnimationInstance*) const; + void dataContext(DataContext* dataContext, DataContext* parent); + void internalDataContext(DataContext* dataContext, DataContext* parent, bool isRoot); + void dataContextFromInstance(ViewModelInstance* viewModelInstance, DataContext* parent); + void dataContextFromInstance(ViewModelInstance* viewModelInstance, + DataContext* parent, + bool isRoot); + void dataContextFromInstance(ViewModelInstance* viewModelInstance); + void addDataBind(DataBind* dataBind); + void populateDataBinds(std::vector* dataBinds); + void sortDataBinds(std::vector dataBinds); + bool hasAudio() const; template T* find(const std::string& name) { @@ -209,7 +273,10 @@ class Artboard : public ArtboardBase, public CoreContext, public ShapePaintConta artboardClone->m_Factory = m_Factory; artboardClone->m_FrameOrigin = m_FrameOrigin; + artboardClone->m_DataContext = m_DataContext; artboardClone->m_IsInstance = true; + artboardClone->m_originalWidth = m_originalWidth; + artboardClone->m_originalHeight = m_originalHeight; std::vector& cloneObjects = artboardClone->m_Objects; cloneObjects.push_back(artboardClone.get()); @@ -222,6 +289,17 @@ class Artboard : public ArtboardBase, public CoreContext, public ShapePaintConta { auto object = *itr; cloneObjects.push_back(object == nullptr ? nullptr : object->clone()); + // For each object, clone its data bind objects and target their clones + for (auto dataBind : m_DataBinds) + { + if (dataBind->target() == object) + { + auto dataBindClone = static_cast(dataBind->clone()); + dataBindClone->target(cloneObjects.back()); + dataBindClone->converter(dataBind->converter()); + artboardClone->m_DataBinds.push_back(dataBindClone); + } + } } } @@ -258,12 +336,46 @@ class Artboard : public ArtboardBase, public CoreContext, public ShapePaintConta /// relative to the bounds. void frameOrigin(bool value); + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + bool result = ArtboardBase::deserialize(propertyKey, reader); + switch (propertyKey) + { + case widthPropertyKey: + m_originalWidth = width(); + break; + case heightPropertyKey: + m_originalHeight = height(); + break; + default: + break; + } + return result; + } + StatusCode import(ImportStack& importStack) override; + float volume() const; + void volume(float value); + #ifdef EXTERNAL_RIVE_AUDIO_ENGINE rcp audioEngine() const; void audioEngine(rcp audioEngine); #endif + +#ifdef WITH_RIVE_LAYOUT + void propagateSize() override; +#endif +private: + float m_volume = 1.0f; +#ifdef WITH_RIVE_TOOLS + ArtboardCallback m_layoutChangedCallback = nullptr; + ArtboardCallback m_layoutDirtyCallback = nullptr; + +public: + void onLayoutChanged(ArtboardCallback callback) { m_layoutChangedCallback = callback; } + void onLayoutDirty(ArtboardCallback callback) { m_layoutDirtyCallback = callback; } +#endif }; class ArtboardInstance : public Artboard @@ -289,6 +401,14 @@ class ArtboardInstance : public Artboard // 3. first animation instance // 4. nullptr std::unique_ptr defaultScene(); + + SMIInput* input(const std::string& name, const std::string& path); + template + InstType* getNamedInput(const std::string& name, const std::string& path); + SMIBool* getBool(const std::string& name, const std::string& path); + SMINumber* getNumber(const std::string& name, const std::string& path); + SMITrigger* getTrigger(const std::string& name, const std::string& path); + TextValueRun* getTextRun(const std::string& name, const std::string& path); }; } // namespace rive diff --git a/include/rive/assets/audio_asset.hpp b/include/rive/assets/audio_asset.hpp index 4e00ce54..bc8d89a5 100644 --- a/include/rive/assets/audio_asset.hpp +++ b/include/rive/assets/audio_asset.hpp @@ -2,6 +2,7 @@ #define _RIVE_AUDIO_ASSET_HPP_ #include "rive/generated/assets/audio_asset_base.hpp" #include "rive/audio/audio_source.hpp" +#include "rive/audio/audio_engine.hpp" namespace rive { @@ -13,17 +14,16 @@ class AudioAsset : public AudioAssetBase bool decode(SimpleArray&, Factory*) override; std::string fileExtension() const override; -#ifdef WITH_RIVE_AUDIO #ifdef TESTING bool hasAudioSource() { return m_audioSource != nullptr; } #endif rcp audioSource() { return m_audioSource; } void audioSource(rcp source) { m_audioSource = source; } + void stop(rcp engine); private: rcp m_audioSource; -#endif }; } // namespace rive diff --git a/include/rive/assets/export_audio.hpp b/include/rive/assets/export_audio.hpp new file mode 100644 index 00000000..7642863a --- /dev/null +++ b/include/rive/assets/export_audio.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_EXPORT_AUDIO_HPP_ +#define _RIVE_EXPORT_AUDIO_HPP_ +#include "rive/generated/assets/export_audio_base.hpp" +#include +namespace rive +{ +class ExportAudio : public ExportAudioBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/assets/file_asset.hpp b/include/rive/assets/file_asset.hpp index a8ed721e..22915f40 100644 --- a/include/rive/assets/file_asset.hpp +++ b/include/rive/assets/file_asset.hpp @@ -50,6 +50,7 @@ class FileAsset : public FileAssetBase } } + std::string uniqueName() const; std::string uniqueFilename() const; }; } // namespace rive diff --git a/include/rive/audio/audio_engine.hpp b/include/rive/audio/audio_engine.hpp index de7b21d4..e709ec7e 100644 --- a/include/rive/audio/audio_engine.hpp +++ b/include/rive/audio/audio_engine.hpp @@ -7,20 +7,25 @@ #include #include #include +#include typedef struct ma_engine ma_engine; typedef struct ma_sound ma_sound; typedef struct ma_device ma_device; +typedef struct ma_node_base ma_node_base; +typedef struct ma_context ma_context; namespace rive { class AudioSound; class AudioSource; - +class LevelsNode; +class Artboard; class AudioEngine : public RefCnt { friend class AudioSound; friend class AudioSource; + friend class LevelsNode; public: static const uint32_t defaultNumChannels = 2; @@ -39,26 +44,48 @@ class AudioEngine : public RefCnt rcp play(rcp source, uint64_t startTime, uint64_t endTime, - uint64_t soundStartTime); + uint64_t soundStartTime, + Artboard* artboard = nullptr); - static rcp RuntimeEngine(); + static rcp RuntimeEngine(bool makeWhenNecessary = true); #ifdef EXTERNAL_RIVE_AUDIO_ENGINE bool readAudioFrames(float* frames, uint64_t numFrames, uint64_t* framesRead = nullptr); bool sumAudioFrames(float* frames, uint64_t numFrames); #endif +#ifdef WITH_RIVE_AUDIO_TOOLS + void initLevelMonitor(); + void levels(Span levels); + float level(uint32_t channel); +#endif + + void start(); + void stop(); + void stop(Artboard* artboard); + +#ifdef TESTING + size_t playingSoundCount(); +#endif private: - AudioEngine(ma_engine* engine); + AudioEngine(ma_engine* engine, ma_context* context); ma_device* m_device; ma_engine* m_engine; + ma_context* m_context; + std::mutex m_mutex; - std::vector> m_completedSounds; + void soundCompleted(rcp sound); + void unlinkSound(rcp sound); - void completeSound(rcp sound); - void purgeCompletedSounds(); + std::vector> m_completedSounds; + rcp m_playingSoundsHead; static void SoundCompleted(void* pUserData, ma_sound* pSound); +#ifdef WITH_RIVE_AUDIO_TOOLS + void measureLevels(const float* frames, uint32_t frameCount); + std::vector m_levels; + LevelsNode* m_levelMonitor = nullptr; +#endif #ifdef EXTERNAL_RIVE_AUDIO_ENGINE std::vector m_readFrames; #endif diff --git a/include/rive/audio/audio_format.hpp b/include/rive/audio/audio_format.hpp index ab73fe54..330dffea 100644 --- a/include/rive/audio/audio_format.hpp +++ b/include/rive/audio/audio_format.hpp @@ -1,4 +1,3 @@ -#ifdef WITH_RIVE_AUDIO #ifndef _RIVE_AUDIO_FORMAT_HPP_ #define _RIVE_AUDIO_FORMAT_HPP_ namespace rive @@ -13,5 +12,4 @@ enum class AudioFormat : unsigned int buffered }; } -#endif #endif \ No newline at end of file diff --git a/include/rive/audio/audio_sound.hpp b/include/rive/audio/audio_sound.hpp index 2a409f62..96d63f39 100644 --- a/include/rive/audio/audio_sound.hpp +++ b/include/rive/audio/audio_sound.hpp @@ -4,10 +4,98 @@ #include "miniaudio.h" #include "rive/refcnt.hpp" +#include "rive/audio/audio_source.hpp" namespace rive { class AudioEngine; +class Artboard; + +struct ma_end_clipped_decoder +{ + ma_data_source_base base; + ma_decoder decoder; + ma_uint64 frameCursor; + ma_uint64 endFrame; +}; + +static ma_result ma_end_clipped_decoder_read(ma_data_source* pDataSource, + void* pFramesOut, + ma_uint64 frameCount, + ma_uint64* pFramesRead) +{ + ma_end_clipped_decoder* clipped = (ma_end_clipped_decoder*)pDataSource; + + ma_result result = + ma_decoder_read_pcm_frames(&clipped->decoder, pFramesOut, frameCount, pFramesRead); + + clipped->frameCursor += *pFramesRead; + if (clipped->frameCursor >= clipped->endFrame) + { + ma_uint64 overflow = clipped->frameCursor - clipped->endFrame; + if (*pFramesRead > overflow) + { + *pFramesRead -= overflow; + } + else + { + *pFramesRead = 0; + return MA_AT_END; + } + } + + return result; +} + +static ma_result ma_end_clipped_decoder_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) +{ + ma_end_clipped_decoder* clipped = (ma_end_clipped_decoder*)pDataSource; + ma_result result = ma_decoder_seek_to_pcm_frame(&clipped->decoder, frameIndex); + if (result != MA_SUCCESS) + { + return result; + } + + clipped->frameCursor = frameIndex; + return result; +} + +static ma_result ma_end_clipped_decoder_get_data_format(ma_data_source* pDataSource, + ma_format* pFormat, + ma_uint32* pChannels, + ma_uint32* pSampleRate, + ma_channel* pChannelMap, + size_t channelMapCap) +{ + ma_end_clipped_decoder* clipped = (ma_end_clipped_decoder*)pDataSource; + return ma_decoder_get_data_format(&clipped->decoder, + pFormat, + pChannels, + pSampleRate, + pChannelMap, + channelMapCap); +} + +static ma_result ma_end_clipped_decoder_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) +{ + ma_end_clipped_decoder* clipped = (ma_end_clipped_decoder*)pDataSource; + *pCursor = clipped->frameCursor; + return MA_SUCCESS; +} + +static ma_result ma_end_clipped_decoder_get_length(ma_data_source* pDataSource, ma_uint64* pLength) +{ + ma_end_clipped_decoder* clipped = (ma_end_clipped_decoder*)pDataSource; + return ma_decoder_get_length_in_pcm_frames(&clipped->decoder, pLength); +} + +static ma_data_source_vtable g_ma_end_clipped_decoder_vtable = { + ma_end_clipped_decoder_read, + ma_end_clipped_decoder_seek, + ma_end_clipped_decoder_get_data_format, + ma_end_clipped_decoder_get_cursor, + ma_end_clipped_decoder_get_length}; + class AudioSound : public RefCnt { friend class AudioEngine; @@ -16,19 +104,28 @@ class AudioSound : public RefCnt bool seek(uint64_t timeInFrames); ~AudioSound(); void stop(uint64_t fadeTimeInFrames = 0); + float volume(); + void volume(float value); + bool completed() const; private: - AudioSound(rcp engine); - void complete(); + AudioSound(AudioEngine* engine, rcp source, Artboard* artboard); + ma_end_clipped_decoder* clippedDecoder() { return &m_decoder; } + ma_audio_buffer* buffer() { return &m_buffer; } + ma_sound* sound() { return &m_sound; } + void dispose(); - rcp m_engine; - ma_decoder m_decoder; + ma_end_clipped_decoder m_decoder; ma_audio_buffer m_buffer; ma_sound m_sound; + rcp m_source; - ma_decoder* decoder() { return &m_decoder; } - ma_audio_buffer* buffer() { return &m_buffer; } - ma_sound* sound() { return &m_sound; } + // This is storage used by the AudioEngine. + bool m_isDisposed; + rcp m_nextPlaying; + rcp m_prevPlaying; + AudioEngine* m_engine; + Artboard* m_artboard; }; } // namespace rive diff --git a/include/rive/audio/audio_source.hpp b/include/rive/audio/audio_source.hpp index b38984b8..d6153a38 100644 --- a/include/rive/audio/audio_source.hpp +++ b/include/rive/audio/audio_source.hpp @@ -1,4 +1,3 @@ -#ifdef WITH_RIVE_AUDIO #ifndef _RIVE_AUDIO_SOURCE_HPP_ #define _RIVE_AUDIO_SOURCE_HPP_ @@ -26,24 +25,41 @@ class AudioSource : public RefCnt // the AudioSource deletes. AudioSource(rive::Span samples, uint32_t numChannels, uint32_t sampleRate); +#ifdef WITH_RIVE_AUDIO rcp makeReader(uint32_t numChannels, uint32_t sampleRate); +#endif uint32_t channels(); uint32_t sampleRate(); AudioFormat format() const; - const rive::Span bytes() const { return m_fileBytes; } + const rive::Span bytes() const + { +#ifdef WITH_RIVE_AUDIO + return m_fileBytes; +#else + return rive::Span(nullptr, 0); +#endif + } const rive::Span bufferedSamples() const; - bool isBuffered() const { return m_isBuffered; } + bool isBuffered() const + { +#ifdef WITH_RIVE_AUDIO + return m_isBuffered; +#else + return false; +#endif + } private: +#ifdef WITH_RIVE_AUDIO bool m_isBuffered; uint32_t m_channels; uint32_t m_sampleRate; rive::Span m_fileBytes; rive::SimpleArray m_ownedBytes; +#endif }; } // namespace rive -#endif #endif \ No newline at end of file diff --git a/include/rive/audio_event.hpp b/include/rive/audio_event.hpp index cd67c6c5..2844e9c6 100644 --- a/include/rive/audio_event.hpp +++ b/include/rive/audio_event.hpp @@ -13,6 +13,7 @@ class AudioEvent : public AudioEventBase, public FileAssetReferencer void setAsset(FileAsset* asset) override; uint32_t assetId() override; void trigger(const CallbackData& value) override; + void play(); #ifdef TESTING AudioAsset* asset() const { return (AudioAsset*)m_fileAsset; } diff --git a/include/rive/bounds_provider.hpp b/include/rive/bounds_provider.hpp new file mode 100644 index 00000000..7f3ca83a --- /dev/null +++ b/include/rive/bounds_provider.hpp @@ -0,0 +1,17 @@ +#ifndef _RIVE_BOUNDS_PROVIDER_HPP_ +#define _RIVE_BOUNDS_PROVIDER_HPP_ + +#include "rive/math/aabb.hpp" +#include "rive/math/mat2d.hpp" + +namespace rive +{ + +class BoundsProvider +{ +public: + virtual ~BoundsProvider() {} + virtual AABB computeBounds(Mat2D toParent); +}; +} // namespace rive +#endif \ No newline at end of file diff --git a/include/rive/component.hpp b/include/rive/component.hpp index 0aee6eeb..7488dca9 100644 --- a/include/rive/component.hpp +++ b/include/rive/component.hpp @@ -2,8 +2,10 @@ #define _RIVE_COMPONENT_HPP_ #include "rive/component_dirt.hpp" #include "rive/generated/component_base.hpp" +#include "rive/dependency_helper.hpp" #include +#include namespace rive { @@ -16,7 +18,6 @@ class Component : public ComponentBase private: ContainerComponent* m_Parent = nullptr; - std::vector m_Dependents; unsigned int m_GraphOrder; Artboard* m_Artboard = nullptr; @@ -25,11 +26,12 @@ class Component : public ComponentBase ComponentDirt m_Dirt = ComponentDirt::Filthy; public: + DependencyHelper m_DependencyHelper; virtual bool collapse(bool value); inline Artboard* artboard() const { return m_Artboard; } StatusCode onAddedDirty(CoreContext* context) override; inline ContainerComponent* parent() const { return m_Parent; } - const std::vector& dependents() const { return m_Dependents; } + const std::vector& dependents() const { return m_DependencyHelper.dependents(); } void addDependent(Component* component); diff --git a/include/rive/component_dirt.hpp b/include/rive/component_dirt.hpp index 73f465e3..ccd64c0c 100644 --- a/include/rive/component_dirt.hpp +++ b/include/rive/component_dirt.hpp @@ -56,10 +56,15 @@ enum class ComponentDirt : unsigned short /// Used by the gradients track when the stops need to be re-ordered. Stops = 1 << 10, + /// Used by data binds to track the value has changed. + Bindings = 1 << 11, + /// Blend modes need to be updated // TODO: do we need this? // BlendMode = 1 << 9, + LayoutStyle = 1 << 11, + /// All dirty. Every flag (apart from Collapsed) is set. Filthy = 0xFFFE }; diff --git a/include/rive/constraints/distance_constraint.hpp b/include/rive/constraints/distance_constraint.hpp index 83a9f9ff..5af1a705 100644 --- a/include/rive/constraints/distance_constraint.hpp +++ b/include/rive/constraints/distance_constraint.hpp @@ -8,6 +8,8 @@ class DistanceConstraint : public DistanceConstraintBase { public: void constrain(TransformComponent* component) override; + void distanceChanged() override; + void modeValueChanged() override; }; } // namespace rive diff --git a/include/rive/constraints/follow_path_constraint.hpp b/include/rive/constraints/follow_path_constraint.hpp index 604e5782..a59b0f67 100644 --- a/include/rive/constraints/follow_path_constraint.hpp +++ b/include/rive/constraints/follow_path_constraint.hpp @@ -2,13 +2,11 @@ #define _RIVE_FOLLOW_PATH_CONSTRAINT_HPP_ #include "rive/generated/constraints/follow_path_constraint_base.hpp" #include "rive/math/transform_components.hpp" -#include "rive/shapes/metrics_path.hpp" -#include +#include "rive/math/contour_measure.hpp" namespace rive { class FollowPathConstraint : public FollowPathConstraintBase { - public: void distanceChanged() override; void orientChanged() override; diff --git a/include/rive/container_component.hpp b/include/rive/container_component.hpp index f1879260..04cf246a 100644 --- a/include/rive/container_component.hpp +++ b/include/rive/container_component.hpp @@ -2,17 +2,24 @@ #define _RIVE_CONTAINER_COMPONENT_HPP_ #include "rive/generated/container_component_base.hpp" #include +#include + namespace rive { class ContainerComponent : public ContainerComponentBase { -private: - std::vector m_children; - public: const std::vector& children() const { return m_children; } virtual void addChild(Component* component); bool collapse(bool value) override; + + // Returns true if it searched through all of its children. predicate can + // return false to stop searching. + bool forAll(std::function predicate); + bool forEachChild(std::function predicate); + +private: + std::vector m_children; }; } // namespace rive diff --git a/include/rive/core/binary_data_reader.hpp b/include/rive/core/binary_data_reader.hpp new file mode 100644 index 00000000..beae449a --- /dev/null +++ b/include/rive/core/binary_data_reader.hpp @@ -0,0 +1,38 @@ +#ifndef _RIVE_CORE_BINARY_DATA_READER_HPP_ +#define _RIVE_CORE_BINARY_DATA_READER_HPP_ + +#include +#include + +namespace rive +{ +class BinaryDataReader +{ +private: + uint8_t* m_Position; + uint8_t* m_End; + bool m_Overflowed; + size_t m_Length; + + void overflow(); + +public: + BinaryDataReader(uint8_t* bytes, size_t length); + bool didOverflow() const; + bool isEOF() const { return m_Position >= m_End; } + const uint8_t* position() const { return m_Position; } + + size_t lengthInBytes() const; + + uint64_t readVarUint(); + uint32_t readVarUint32(); + double readFloat64(); + float readFloat32(); + uint8_t readByte(); + uint32_t readUint32(); + void complete(uint8_t* bytes, size_t length); + void reset(uint8_t* bytes); +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/core/binary_reader.hpp b/include/rive/core/binary_reader.hpp index c0c2c3be..00687da8 100644 --- a/include/rive/core/binary_reader.hpp +++ b/include/rive/core/binary_reader.hpp @@ -49,6 +49,7 @@ class BinaryReader } return static_cast(value); } + void reset(); }; } // namespace rive diff --git a/include/rive/core/binary_stream.hpp b/include/rive/core/binary_stream.hpp new file mode 100644 index 00000000..f582ccf3 --- /dev/null +++ b/include/rive/core/binary_stream.hpp @@ -0,0 +1,20 @@ +#ifndef _RIVE_CORE_BINARY_STREAM_HPP_ +#define _RIVE_CORE_BINARY_STREAM_HPP_ + +#include +#include + +namespace rive +{ +// Used to write binary chunks to an underlying stream, makes no assumptions +// regarding storage/streaming it can flush the contents as it needs. +class BinaryStream +{ +public: + virtual void write(const uint8_t* bytes, std::size_t length) = 0; + virtual void flush() = 0; + virtual void clear() = 0; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/core/binary_writer.hpp b/include/rive/core/binary_writer.hpp new file mode 100644 index 00000000..8d87ceef --- /dev/null +++ b/include/rive/core/binary_writer.hpp @@ -0,0 +1,32 @@ +#ifndef _RIVE_CORE_BINARY_WRITER_HPP_ +#define _RIVE_CORE_BINARY_WRITER_HPP_ + +#include +#include + +namespace rive +{ +class BinaryStream; +class BinaryWriter +{ +private: + BinaryStream* m_Stream; + +public: + BinaryWriter(BinaryStream* stream); + ~BinaryWriter(); + void write(float value); + void writeFloat(float value); + void write(double value); + void writeVarUint(uint64_t value); + void writeVarUint(uint32_t value); + void write(const uint8_t* bytes, std::size_t length); + void write(uint8_t value); + void writeDouble(double value); + void write(uint16_t value); + void write(uint32_t value); + void clear(); +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/core/field_types/core_callback_type.hpp b/include/rive/core/field_types/core_callback_type.hpp index 1c8ba627..a6bb93f4 100644 --- a/include/rive/core/field_types/core_callback_type.hpp +++ b/include/rive/core/field_types/core_callback_type.hpp @@ -9,6 +9,7 @@ class CallbackContext public: virtual ~CallbackContext() {} virtual void reportEvent(Event* event, float secondsDelay = 0.0f) {} + virtual bool playsAudio() { return false; } }; class CallbackData diff --git a/include/rive/core/reader.h b/include/rive/core/reader.h index f6969ba9..45c8ffda 100644 --- a/include/rive/core/reader.h +++ b/include/rive/core/reader.h @@ -35,6 +35,29 @@ inline size_t decode_uint_leb(const uint8_t* buf, const uint8_t* buf_end, uint64 return p - buf; } +/* Decode an unsigned int LEB128 at buf into r, returning the nr of bytes read. + */ +inline size_t decode_uint_leb32(const uint8_t* buf, const uint8_t* buf_end, uint32_t* r) +{ + const uint8_t* p = buf; + uint8_t shift = 0; + uint32_t result = 0; + uint8_t byte; + + do + { + if (p >= buf_end) + { + return 0; + } + byte = *p++; + result |= ((uint32_t)(byte & 0x7f)) << shift; + shift += 7; + } while ((byte & 0x80) != 0); + *r = result; + return p - buf; +} + /* Decodes a string */ inline uint64_t decode_string(uint64_t str_len, @@ -57,6 +80,27 @@ inline uint64_t decode_string(uint64_t str_len, return str_len; } +/* Decodes a double (8 bytes) + */ +inline size_t decode_double(const uint8_t* buf, const uint8_t* buf_end, double* r) +{ + // Return zero bytes read on buffer overflow + if (buf_end - buf < sizeof(double)) + { + return 0; + } + if (is_big_endian()) + { + uint8_t inverted[8] = {buf[7], buf[6], buf[5], buf[4], buf[3], buf[2], buf[1], buf[0]}; + memcpy(r, inverted, sizeof(double)); + } + else + { + memcpy(r, buf, sizeof(double)); + } + return sizeof(double); +} + /* Decodes a float (4 bytes) */ inline size_t decode_float(const uint8_t* buf, const uint8_t* buf_end, float* r) @@ -110,4 +154,4 @@ inline size_t decode_uint_32(const uint8_t* buf, const uint8_t* buf_end, uint32_ memcpy(r, buf, sizeof(uint32_t)); } return sizeof(uint32_t); -} +} \ No newline at end of file diff --git a/include/rive/core/vector_binary_writer.hpp b/include/rive/core/vector_binary_writer.hpp new file mode 100644 index 00000000..34ba6491 --- /dev/null +++ b/include/rive/core/vector_binary_writer.hpp @@ -0,0 +1,43 @@ +#ifndef _RIVE_CORE_VECTOR_BINARY_WRITER_HPP_ +#define _RIVE_CORE_VECTOR_BINARY_WRITER_HPP_ + +#include "rive/core/binary_stream.hpp" +#include "rive/core/binary_writer.hpp" +#include + +namespace rive +{ +class VectorBinaryWriter : public BinaryStream, public BinaryWriter +{ +private: + std::vector* m_WriteBuffer; + std::size_t m_Start; + size_t m_pos = 0; + +public: + VectorBinaryWriter(std::vector* buffer) : + BinaryWriter(this), m_WriteBuffer(buffer), m_Start(m_WriteBuffer->size()) + {} + + uint8_t* buffer() const { return &(*m_WriteBuffer)[m_Start]; } + std::size_t bufferSize() const { return m_WriteBuffer->size() - m_Start; } + + std::size_t start() const { return m_Start; } + size_t size() const { return m_pos; } + + using BinaryWriter::write; + void write(const uint8_t* bytes, std::size_t length) override + { + auto end = m_pos; + if (m_WriteBuffer->size() < end + length) + { + m_WriteBuffer->resize(end + length); + } + std::memcpy(&((*m_WriteBuffer)[end]), bytes, length); + m_pos += length; + } + void flush() override {} + void clear() override { m_pos = 0; } +}; +} // namespace rive +#endif \ No newline at end of file diff --git a/include/rive/data_bind/bindable_property.hpp b/include/rive/data_bind/bindable_property.hpp new file mode 100644 index 00000000..cbfb148b --- /dev/null +++ b/include/rive/data_bind/bindable_property.hpp @@ -0,0 +1,12 @@ +#ifndef _RIVE_BINDABLE_PROPERTY_HPP_ +#define _RIVE_BINDABLE_PROPERTY_HPP_ +#include "rive/generated/data_bind/bindable_property_base.hpp" +#include "rive/data_bind/data_bind.hpp" +#include +namespace rive +{ +class BindableProperty : public BindablePropertyBase +{}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/bindable_property_boolean.hpp b/include/rive/data_bind/bindable_property_boolean.hpp new file mode 100644 index 00000000..18037b6c --- /dev/null +++ b/include/rive/data_bind/bindable_property_boolean.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_BINDABLE_PROPERTY_BOOLEAN_HPP_ +#define _RIVE_BINDABLE_PROPERTY_BOOLEAN_HPP_ +#include "rive/generated/data_bind/bindable_property_boolean_base.hpp" +#include +namespace rive +{ +class BindablePropertyBoolean : public BindablePropertyBooleanBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/bindable_property_color.hpp b/include/rive/data_bind/bindable_property_color.hpp new file mode 100644 index 00000000..dd128ca2 --- /dev/null +++ b/include/rive/data_bind/bindable_property_color.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_BINDABLE_PROPERTY_COLOR_HPP_ +#define _RIVE_BINDABLE_PROPERTY_COLOR_HPP_ +#include "rive/generated/data_bind/bindable_property_color_base.hpp" +#include +namespace rive +{ +class BindablePropertyColor : public BindablePropertyColorBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/bindable_property_enum.hpp b/include/rive/data_bind/bindable_property_enum.hpp new file mode 100644 index 00000000..48692c2b --- /dev/null +++ b/include/rive/data_bind/bindable_property_enum.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_BINDABLE_PROPERTY_ENUM_HPP_ +#define _RIVE_BINDABLE_PROPERTY_ENUM_HPP_ +#include "rive/generated/data_bind/bindable_property_enum_base.hpp" +#include +namespace rive +{ +class BindablePropertyEnum : public BindablePropertyEnumBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/bindable_property_number.hpp b/include/rive/data_bind/bindable_property_number.hpp new file mode 100644 index 00000000..38a72fdf --- /dev/null +++ b/include/rive/data_bind/bindable_property_number.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_BINDABLE_PROPERTY_NUMBER_HPP_ +#define _RIVE_BINDABLE_PROPERTY_NUMBER_HPP_ +#include "rive/generated/data_bind/bindable_property_number_base.hpp" +#include +namespace rive +{ +class BindablePropertyNumber : public BindablePropertyNumberBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/bindable_property_string.hpp b/include/rive/data_bind/bindable_property_string.hpp new file mode 100644 index 00000000..302caf07 --- /dev/null +++ b/include/rive/data_bind/bindable_property_string.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_BINDABLE_PROPERTY_STRING_HPP_ +#define _RIVE_BINDABLE_PROPERTY_STRING_HPP_ +#include "rive/generated/data_bind/bindable_property_string_base.hpp" +#include +namespace rive +{ +class BindablePropertyString : public BindablePropertyStringBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/context/context_value.hpp b/include/rive/data_bind/context/context_value.hpp new file mode 100644 index 00000000..c26730b2 --- /dev/null +++ b/include/rive/data_bind/context/context_value.hpp @@ -0,0 +1,49 @@ +#ifndef _RIVE_DATA_BIND_CONTEXT_VALUE_HPP_ +#define _RIVE_DATA_BIND_CONTEXT_VALUE_HPP_ +#include "rive/viewmodel/viewmodel_instance_value.hpp" +#include "rive/data_bind/converters/data_converter.hpp" +#include +namespace rive +{ +class DataBindContextValue +{ +protected: + ViewModelInstanceValue* m_source; + DataConverter* m_converter; + DataValue* m_dataValue; + +public: + DataBindContextValue(ViewModelInstanceValue* source, DataConverter* converter); + virtual ~DataBindContextValue(){}; + virtual void applyToSource(Core* component, uint32_t propertyKey, bool isMainDirection); + virtual void apply(Core* component, uint32_t propertyKey, bool isMainDirection){}; + virtual void update(Core* component){}; + virtual DataValue* getTargetValue(Core* target, uint32_t propertyKey) { return nullptr; }; + void updateSourceValue(); + template U getDataValue(DataValue* input) + { + auto dataValue = m_converter != nullptr ? m_converter->convert(input) : input; + if (dataValue->is()) + { + return dataValue->as()->value(); + } + return (new T())->value(); + }; + template U getReverseDataValue(DataValue* input) + { + auto dataValue = m_converter != nullptr ? m_converter->reverseConvert(input) : input; + if (dataValue->is()) + { + return dataValue->as()->value(); + } + return (new T())->value(); + }; + template + U calculateValue(DataValue* input, bool isMainDirection) + { + return isMainDirection ? getDataValue(input) : getReverseDataValue(input); + }; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/context/context_value_boolean.hpp b/include/rive/data_bind/context/context_value_boolean.hpp new file mode 100644 index 00000000..678e7fc5 --- /dev/null +++ b/include/rive/data_bind/context/context_value_boolean.hpp @@ -0,0 +1,16 @@ +#ifndef _RIVE_DATA_BIND_CONTEXT_VALUE_BOOLEAN_HPP_ +#define _RIVE_DATA_BIND_CONTEXT_VALUE_BOOLEAN_HPP_ +#include "rive/data_bind/context/context_value.hpp" +namespace rive +{ +class DataBindContextValueBoolean : public DataBindContextValue +{ + +public: + DataBindContextValueBoolean(ViewModelInstanceValue* source, DataConverter* converter); + void apply(Core* component, uint32_t propertyKey, bool isMainDirection) override; + DataValue* getTargetValue(Core* target, uint32_t propertyKey) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/context/context_value_color.hpp b/include/rive/data_bind/context/context_value_color.hpp new file mode 100644 index 00000000..207062a8 --- /dev/null +++ b/include/rive/data_bind/context/context_value_color.hpp @@ -0,0 +1,16 @@ +#ifndef _RIVE_DATA_BIND_CONTEXT_VALUE_COLOR_HPP_ +#define _RIVE_DATA_BIND_CONTEXT_VALUE_COLOR_HPP_ +#include "rive/data_bind/context/context_value.hpp" +namespace rive +{ +class DataBindContextValueColor : public DataBindContextValue +{ + +public: + DataBindContextValueColor(ViewModelInstanceValue* source, DataConverter* converter); + void apply(Core* component, uint32_t propertyKey, bool isMainDirection) override; + DataValue* getTargetValue(Core* target, uint32_t propertyKey) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/context/context_value_enum.hpp b/include/rive/data_bind/context/context_value_enum.hpp new file mode 100644 index 00000000..dd097b23 --- /dev/null +++ b/include/rive/data_bind/context/context_value_enum.hpp @@ -0,0 +1,16 @@ +#ifndef _RIVE_DATA_BIND_CONTEXT_VALUE_ENUM_HPP_ +#define _RIVE_DATA_BIND_CONTEXT_VALUE_ENUM_HPP_ +#include "rive/data_bind/context/context_value.hpp" +namespace rive +{ +class DataBindContextValueEnum : public DataBindContextValue +{ + +public: + DataBindContextValueEnum(ViewModelInstanceValue* source, DataConverter* converter); + void apply(Core* component, uint32_t propertyKey, bool isMainDirection) override; + DataValue* getTargetValue(Core* target, uint32_t propertyKey) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/context/context_value_list.hpp b/include/rive/data_bind/context/context_value_list.hpp new file mode 100644 index 00000000..5dd95861 --- /dev/null +++ b/include/rive/data_bind/context/context_value_list.hpp @@ -0,0 +1,33 @@ +#ifndef _RIVE_DATA_BIND_CONTEXT_VALUE_LIST_HPP_ +#define _RIVE_DATA_BIND_CONTEXT_VALUE_LIST_HPP_ +#include "rive/data_bind/context/context_value.hpp" +#include "rive/data_bind/context/context_value_list_item.hpp" +#include "rive/artboard.hpp" +#include "rive/animation/state_machine_instance.hpp" +#include "rive/viewmodel/viewmodel_instance_list_item.hpp" +namespace rive +{ +class DataBindContextValueList : public DataBindContextValue +{ + +public: + DataBindContextValueList(ViewModelInstanceValue* source, DataConverter* converter); + void apply(Core* component, uint32_t propertyKey, bool isMainDirection) override; + void update(Core* target) override; + virtual void applyToSource(Core* component, + uint32_t propertyKey, + bool isMainDirection) override; + +private: + std::vector> m_ListItemsCache; + void insertItem(Core* target, ViewModelInstanceListItem* viewModelInstanceListItem, int index); + void swapItems(Core* target, int index1, int index2); + void popItem(Core* target); + std::unique_ptr createArtboard(Component* target, + Artboard* artboard, + ViewModelInstanceListItem* listItem) const; + std::unique_ptr createStateMachineInstance(ArtboardInstance* artboard); +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/context/context_value_list_item.hpp b/include/rive/data_bind/context/context_value_list_item.hpp new file mode 100644 index 00000000..f15493b3 --- /dev/null +++ b/include/rive/data_bind/context/context_value_list_item.hpp @@ -0,0 +1,25 @@ +#ifndef _RIVE_DATA_BIND_CONTEXT_VALUE_LIST_ITEM_HPP_ +#define _RIVE_DATA_BIND_CONTEXT_VALUE_LIST_ITEM_HPP_ +#include "rive/data_bind/context/context_value.hpp" +#include "rive/artboard.hpp" +#include "rive/animation/state_machine_instance.hpp" +#include "rive/viewmodel/viewmodel_instance_list_item.hpp" +namespace rive +{ +class DataBindContextValueListItem +{ + +private: + std::unique_ptr m_Artboard; + std::unique_ptr m_StateMachine; + ViewModelInstanceListItem* m_ListItem; + +public: + DataBindContextValueListItem(std::unique_ptr artboard, + std::unique_ptr stateMachine, + ViewModelInstanceListItem* listItem); + ViewModelInstanceListItem* listItem() { return m_ListItem; }; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/context/context_value_number.hpp b/include/rive/data_bind/context/context_value_number.hpp new file mode 100644 index 00000000..f3a56fa2 --- /dev/null +++ b/include/rive/data_bind/context/context_value_number.hpp @@ -0,0 +1,16 @@ +#ifndef _RIVE_DATA_BIND_CONTEXT_VALUE_NUMBER_HPP_ +#define _RIVE_DATA_BIND_CONTEXT_VALUE_NUMBER_HPP_ +#include "rive/data_bind/context/context_value.hpp" +namespace rive +{ +class DataBindContextValueNumber : public DataBindContextValue +{ + +public: + DataBindContextValueNumber(ViewModelInstanceValue* source, DataConverter* converter); + void apply(Core* component, uint32_t propertyKey, bool isMainDirection) override; + DataValue* getTargetValue(Core* target, uint32_t propertyKey) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/context/context_value_string.hpp b/include/rive/data_bind/context/context_value_string.hpp new file mode 100644 index 00000000..fb8f3cfc --- /dev/null +++ b/include/rive/data_bind/context/context_value_string.hpp @@ -0,0 +1,16 @@ +#ifndef _RIVE_DATA_BIND_CONTEXT_VALUE_STRING_HPP_ +#define _RIVE_DATA_BIND_CONTEXT_VALUE_STRING_HPP_ +#include "rive/data_bind/context/context_value.hpp" +namespace rive +{ +class DataBindContextValueString : public DataBindContextValue +{ + +public: + DataBindContextValueString(ViewModelInstanceValue* source, DataConverter* converter); + void apply(Core* component, uint32_t propertyKey, bool isMainDirection) override; + DataValue* getTargetValue(Core* target, uint32_t propertyKey) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/converters/data_converter.hpp b/include/rive/data_bind/converters/data_converter.hpp new file mode 100644 index 00000000..bf6deb68 --- /dev/null +++ b/include/rive/data_bind/converters/data_converter.hpp @@ -0,0 +1,18 @@ +#ifndef _RIVE_DATA_CONVERTER_HPP_ +#define _RIVE_DATA_CONVERTER_HPP_ +#include "rive/generated/data_bind/converters/data_converter_base.hpp" +#include "rive/data_bind/data_values/data_value.hpp" +#include +namespace rive +{ +class DataConverter : public DataConverterBase +{ +public: + virtual DataValue* convert(DataValue* value) { return value; }; + virtual DataValue* reverseConvert(DataValue* value) { return value; }; + virtual DataType outputType() { return DataType::none; }; + StatusCode import(ImportStack& importStack) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/converters/data_converter_group.hpp b/include/rive/data_bind/converters/data_converter_group.hpp new file mode 100644 index 00000000..40560857 --- /dev/null +++ b/include/rive/data_bind/converters/data_converter_group.hpp @@ -0,0 +1,28 @@ +#ifndef _RIVE_DATA_CONVERTER_GROUP_HPP_ +#define _RIVE_DATA_CONVERTER_GROUP_HPP_ +#include "rive/generated/data_bind/converters/data_converter_group_base.hpp" +#include "rive/data_bind/converters/data_converter_group_item.hpp" +#include +namespace rive +{ +class DataConverterGroup : public DataConverterGroupBase +{ +public: + DataValue* convert(DataValue* value) override; + DataValue* reverseConvert(DataValue* value) override; + void addItem(DataConverterGroupItem* item); + DataType outputType() override + { + if (m_items.size() > 0) + { + return m_items.back()->converter()->outputType(); + }; + return Super::outputType(); + } + +private: + std::vector m_items; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/converters/data_converter_group_item.hpp b/include/rive/data_bind/converters/data_converter_group_item.hpp new file mode 100644 index 00000000..0a6202d8 --- /dev/null +++ b/include/rive/data_bind/converters/data_converter_group_item.hpp @@ -0,0 +1,20 @@ +#ifndef _RIVE_DATA_CONVERTER_GROUP_ITEM_HPP_ +#define _RIVE_DATA_CONVERTER_GROUP_ITEM_HPP_ +#include "rive/generated/data_bind/converters/data_converter_group_item_base.hpp" +#include "rive/data_bind/converters/data_converter.hpp" +#include +namespace rive +{ +class DataConverterGroupItem : public DataConverterGroupItemBase +{ +public: + StatusCode import(ImportStack& importStack) override; + DataConverter* converter() const { return m_dataConverter; }; + void converter(DataConverter* value) { m_dataConverter = value; }; + +protected: + DataConverter* m_dataConverter; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/converters/data_converter_operation.hpp b/include/rive/data_bind/converters/data_converter_operation.hpp new file mode 100644 index 00000000..9bbb2b4a --- /dev/null +++ b/include/rive/data_bind/converters/data_converter_operation.hpp @@ -0,0 +1,18 @@ +#ifndef _RIVE_DATA_CONVERTER_OPERATION_HPP_ +#define _RIVE_DATA_CONVERTER_OPERATION_HPP_ +#include "rive/generated/data_bind/converters/data_converter_operation_base.hpp" +#include "rive/animation/arithmetic_operation.hpp" +#include +namespace rive +{ +class DataConverterOperation : public DataConverterOperationBase +{ +public: + DataValue* convert(DataValue* value) override; + DataValue* reverseConvert(DataValue* value) override; + DataType outputType() override { return DataType::number; }; + ArithmeticOperation op() const { return (ArithmeticOperation)operationType(); } +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/converters/data_converter_rounder.hpp b/include/rive/data_bind/converters/data_converter_rounder.hpp new file mode 100644 index 00000000..063d38c5 --- /dev/null +++ b/include/rive/data_bind/converters/data_converter_rounder.hpp @@ -0,0 +1,16 @@ +#ifndef _RIVE_DATA_CONVERTER_ROUND_HPP_ +#define _RIVE_DATA_CONVERTER_ROUND_HPP_ +#include "rive/generated/data_bind/converters/data_converter_rounder_base.hpp" +#include "rive/data_bind/data_values/data_value.hpp" +#include +namespace rive +{ +class DataConverterRounder : public DataConverterRounderBase +{ +public: + DataValue* convert(DataValue* value) override; + DataType outputType() override { return DataType::number; }; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/converters/data_converter_to_string.hpp b/include/rive/data_bind/converters/data_converter_to_string.hpp new file mode 100644 index 00000000..0757e60f --- /dev/null +++ b/include/rive/data_bind/converters/data_converter_to_string.hpp @@ -0,0 +1,15 @@ +#ifndef _RIVE_DATA_CONVERTER_TO_STRING_HPP_ +#define _RIVE_DATA_CONVERTER_TO_STRING_HPP_ +#include "rive/generated/data_bind/converters/data_converter_to_string_base.hpp" +#include +namespace rive +{ +class DataConverterToString : public DataConverterToStringBase +{ +public: + DataValue* convert(DataValue* value) override; + DataType outputType() override { return DataType::string; }; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/data_bind.hpp b/include/rive/data_bind/data_bind.hpp new file mode 100644 index 00000000..a62cfd66 --- /dev/null +++ b/include/rive/data_bind/data_bind.hpp @@ -0,0 +1,39 @@ +#ifndef _RIVE_DATA_BIND_HPP_ +#define _RIVE_DATA_BIND_HPP_ +#include "rive/component_dirt.hpp" +#include "rive/generated/data_bind/data_bind_base.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" +#include "rive/data_bind/context/context_value.hpp" +#include "rive/data_bind/data_context.hpp" +#include "rive/data_bind/converters/data_converter.hpp" +#include "rive/data_bind/data_values/data_type.hpp" +#include +namespace rive +{ +class DataBind : public DataBindBase +{ +public: + StatusCode onAddedDirty(CoreContext* context) override; + StatusCode import(ImportStack& importStack) override; + virtual void updateSourceBinding(); + virtual void update(ComponentDirt value); + Core* target() const { return m_target; }; + void target(Core* value) { m_target = value; }; + virtual void bind(); + ComponentDirt dirt() { return m_Dirt; }; + void dirt(ComponentDirt value) { m_Dirt = value; }; + bool addDirt(ComponentDirt value, bool recurse); + DataConverter* converter() const { return m_dataConverter; }; + void converter(DataConverter* value) { m_dataConverter = value; }; + +protected: + ComponentDirt m_Dirt = ComponentDirt::Filthy; + Core* m_target; + ViewModelInstanceValue* m_Source; + std::unique_ptr m_ContextValue; + DataConverter* m_dataConverter; + DataType outputType(); +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/data_bind_context.hpp b/include/rive/data_bind/data_bind_context.hpp new file mode 100644 index 00000000..b7e2ad42 --- /dev/null +++ b/include/rive/data_bind/data_bind_context.hpp @@ -0,0 +1,24 @@ +#ifndef _RIVE_DATA_BIND_CONTEXT_HPP_ +#define _RIVE_DATA_BIND_CONTEXT_HPP_ +#include "rive/generated/data_bind/data_bind_context_base.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" +#include "rive/data_bind/context/context_value.hpp" +#include "rive/data_bind/data_context.hpp" +#include "rive/refcnt.hpp" +#include +namespace rive +{ +class DataBindContext : public DataBindContextBase +{ +protected: + std::vector m_SourcePathIdsBuffer; + +public: + void decodeSourcePathIds(Span value) override; + void copySourcePathIds(const DataBindContextBase& object) override; + void bindFromContext(DataContext* dataContext); + ViewModelInstanceValue* source() { return m_Source; }; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/data_context.hpp b/include/rive/data_bind/data_context.hpp new file mode 100644 index 00000000..5bc65282 --- /dev/null +++ b/include/rive/data_bind/data_context.hpp @@ -0,0 +1,38 @@ +#ifndef _RIVE_DATA_CONTEXT_HPP_ +#define _RIVE_DATA_CONTEXT_HPP_ +#include "rive/viewmodel/viewmodel_instance_value.hpp" +#include "rive/viewmodel/viewmodel_instance.hpp" + +namespace rive +{ +class DataContext +{ +private: + DataContext* m_Parent = nullptr; + std::vector m_ViewModelInstances; + ViewModelInstance* m_ViewModelInstance; + +public: + DataContext(); + DataContext(ViewModelInstance* viewModelInstance); + ~DataContext(); + + DataContext* parent() { return m_Parent; } + void parent(DataContext* value) { m_Parent = value; } + void addViewModelInstance(ViewModelInstance* value); + ViewModelInstanceValue* getViewModelProperty(const std::vector path) const; + ViewModelInstance* getViewModelInstance(const std::vector path) const; + void viewModelInstance(ViewModelInstance* value); + ViewModelInstance* viewModelInstance() { return m_ViewModelInstance; }; + + ViewModelInstanceValue* viewModelValue() + { + if (m_Parent) + { + return m_Parent->viewModelValue(); + } + return nullptr; + } +}; +} // namespace rive +#endif diff --git a/include/rive/data_bind/data_values/data_type.hpp b/include/rive/data_bind/data_values/data_type.hpp new file mode 100644 index 00000000..c1740b5d --- /dev/null +++ b/include/rive/data_bind/data_values/data_type.hpp @@ -0,0 +1,30 @@ +#ifndef _RIVE_DATA_TYPE_HPP_ +#define _RIVE_DATA_TYPE_HPP_ +namespace rive +{ +/// Data types used for converters. +enum class DataType : unsigned int +{ + /// None. + none = 0, + + /// String. + string = 1, + + /// Number. + number = 2, + + /// Bool. + boolean = 3, + + /// Color. + color = 4, + + /// List. + list = 5, + + /// Enum. + enumType = 6 +}; +} // namespace rive +#endif \ No newline at end of file diff --git a/include/rive/data_bind/data_values/data_value.hpp b/include/rive/data_bind/data_values/data_value.hpp new file mode 100644 index 00000000..e4121836 --- /dev/null +++ b/include/rive/data_bind/data_values/data_value.hpp @@ -0,0 +1,21 @@ +#ifndef _RIVE_DATA_VALUE_HPP_ +#define _RIVE_DATA_VALUE_HPP_ +#include "rive/data_bind/data_values/data_type.hpp" + +#include +namespace rive +{ +class DataValue +{ +public: + virtual bool isTypeOf(DataType dataType) const { return false; } + template inline bool is() const { return isTypeOf(T::typeKey); } + template inline T* as() + { + assert(is()); + return static_cast(this); + } +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/data_values/data_value_boolean.hpp b/include/rive/data_bind/data_values/data_value_boolean.hpp new file mode 100644 index 00000000..4fd85750 --- /dev/null +++ b/include/rive/data_bind/data_values/data_value_boolean.hpp @@ -0,0 +1,23 @@ +#ifndef _RIVE_DATA_VALUE_BOOLEAN_HPP_ +#define _RIVE_DATA_VALUE_BOOLEAN_HPP_ +#include "rive/data_bind/data_values/data_value.hpp" + +#include +namespace rive +{ +class DataValueBoolean : public DataValue +{ +private: + bool m_value = false; + +public: + DataValueBoolean(bool value) : m_value(value){}; + DataValueBoolean(){}; + static const DataType typeKey = DataType::boolean; + bool isTypeOf(DataType typeKey) const override { return typeKey == DataType::boolean; } + bool value() { return m_value; }; + void value(bool value) { m_value = value; }; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/data_values/data_value_color.hpp b/include/rive/data_bind/data_values/data_value_color.hpp new file mode 100644 index 00000000..fdc88b5b --- /dev/null +++ b/include/rive/data_bind/data_values/data_value_color.hpp @@ -0,0 +1,23 @@ +#ifndef _RIVE_DATA_VALUE_COLOR_HPP_ +#define _RIVE_DATA_VALUE_COLOR_HPP_ +#include "rive/data_bind/data_values/data_value.hpp" + +#include +namespace rive +{ +class DataValueColor : public DataValue +{ +private: + int m_value = false; + +public: + DataValueColor(int value) : m_value(value){}; + DataValueColor(){}; + static const DataType typeKey = DataType::color; + bool isTypeOf(DataType typeKey) const override { return typeKey == DataType::color; } + int value() { return m_value; }; + void value(int value) { m_value = value; }; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/data_values/data_value_enum.hpp b/include/rive/data_bind/data_values/data_value_enum.hpp new file mode 100644 index 00000000..9c2dbe5f --- /dev/null +++ b/include/rive/data_bind/data_values/data_value_enum.hpp @@ -0,0 +1,26 @@ +#ifndef _RIVE_DATA_VALUE_ENUM_HPP_ +#define _RIVE_DATA_VALUE_ENUM_HPP_ +#include "rive/data_bind/data_values/data_value.hpp" +#include "rive/viewmodel/data_enum.hpp" + +#include +namespace rive +{ +class DataValueEnum : public DataValue +{ +private: + uint32_t m_value = 0; + DataEnum* m_dataEnum; + +public: + DataValueEnum(uint32_t value, DataEnum* dataEnum) : m_value(value), m_dataEnum(dataEnum){}; + DataValueEnum(){}; + static const DataType typeKey = DataType::enumType; + bool isTypeOf(DataType typeKey) const override { return typeKey == DataType::enumType; }; + uint32_t value() { return m_value; }; + void value(uint32_t value) { m_value = value; }; + DataEnum* dataEnum() { return m_dataEnum; }; + void dataEnum(DataEnum* value) { m_dataEnum = value; }; +}; +} // namespace rive +#endif \ No newline at end of file diff --git a/include/rive/data_bind/data_values/data_value_number.hpp b/include/rive/data_bind/data_values/data_value_number.hpp new file mode 100644 index 00000000..90a3b5dd --- /dev/null +++ b/include/rive/data_bind/data_values/data_value_number.hpp @@ -0,0 +1,23 @@ +#ifndef _RIVE_DATA_VALUE_NUMBER_HPP_ +#define _RIVE_DATA_VALUE_NUMBER_HPP_ +#include "rive/data_bind/data_values/data_value.hpp" + +#include +namespace rive +{ +class DataValueNumber : public DataValue +{ +private: + float m_value = 0; + +public: + DataValueNumber(float value) : m_value(value){}; + DataValueNumber(){}; + static const DataType typeKey = DataType::number; + bool isTypeOf(DataType typeKey) const override { return typeKey == DataType::number; } + float value() { return m_value; }; + void value(float value) { m_value = value; }; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/data_bind/data_values/data_value_string.hpp b/include/rive/data_bind/data_values/data_value_string.hpp new file mode 100644 index 00000000..7bf2676c --- /dev/null +++ b/include/rive/data_bind/data_values/data_value_string.hpp @@ -0,0 +1,22 @@ +#ifndef _RIVE_DATA_VALUE_STRING_HPP_ +#define _RIVE_DATA_VALUE_STRING_HPP_ +#include "rive/data_bind/data_values/data_value.hpp" + +#include +namespace rive +{ +class DataValueString : public DataValue +{ +private: + std::string m_value = ""; + +public: + DataValueString(std::string value) : m_value(value){}; + DataValueString(){}; + static const DataType typeKey = DataType::string; + bool isTypeOf(DataType typeKey) const override { return typeKey == DataType::string; }; + std::string value() { return m_value; }; + void value(std::string value) { m_value = value; }; +}; +} // namespace rive +#endif \ No newline at end of file diff --git a/include/rive/data_bind_flags.hpp b/include/rive/data_bind_flags.hpp new file mode 100644 index 00000000..2d8561fd --- /dev/null +++ b/include/rive/data_bind_flags.hpp @@ -0,0 +1,29 @@ +#ifndef _RIVE_DATA_BIND_FLAGS_HPP_ +#define _RIVE_DATA_BIND_FLAGS_HPP_ + +#include "rive/enum_bitset.hpp" + +namespace rive +{ +enum class DataBindFlags : unsigned short +{ + /// Whether the main binding direction is to source (0) or to target (1) + Direction = 1 << 0, + + /// Whether the binding direction is twoWay + TwoWay = 1 << 1, + + /// Whether the binding happens only once + Once = 1 << 2, + + /// Flag if set to target + ToTarget = 0, + + /// Flag if set to source + ToSource = 1 << 0, + +}; + +RIVE_MAKE_ENUM_BITSET(DataBindFlags) +} // namespace rive +#endif diff --git a/include/rive/dependency_helper.hpp b/include/rive/dependency_helper.hpp new file mode 100644 index 00000000..59ef805d --- /dev/null +++ b/include/rive/dependency_helper.hpp @@ -0,0 +1,46 @@ +#ifndef _RIVE_DEPENDENCY_HELPER_HPP_ +#define _RIVE_DEPENDENCY_HELPER_HPP_ + +#include "rive/component_dirt.hpp" + +namespace rive +{ +class Component; +// class DependencyRoot +// { +// public: +// virtual void onComponentDirty(Component* component) {}; +// }; + +template class DependencyHelper +{ + std::vector m_Dependents; + T* m_dependecyRoot; + +public: + void dependecyRoot(T* value) { m_dependecyRoot = value; } + DependencyHelper(T* value) : m_dependecyRoot(value) {} + DependencyHelper() {} + void addDependent(U* component) + { + // Make it's not already a dependent. + if (std::find(m_Dependents.begin(), m_Dependents.end(), component) != m_Dependents.end()) + { + return; + } + m_Dependents.push_back(component); + } + void addDirt(ComponentDirt value) + { + for (auto d : m_Dependents) + { + d->addDirt(value, true); + } + } + + void onComponentDirty(U* component) { m_dependecyRoot->onComponentDirty(component); } + + const std::vector& dependents() const { return m_Dependents; } +}; +} // namespace rive +#endif diff --git a/include/rive/dependency_sorter.hpp b/include/rive/dependency_sorter.hpp index 3c9cea3a..17ac8254 100644 --- a/include/rive/dependency_sorter.hpp +++ b/include/rive/dependency_sorter.hpp @@ -15,6 +15,7 @@ class DependencySorter public: void sort(Component* root, std::vector& order); + void sort(std::vector roots, std::vector& order); bool visit(Component* component, std::vector& order); }; } // namespace rive diff --git a/include/rive/drawable.hpp b/include/rive/drawable.hpp index 547d6b54..28220df5 100644 --- a/include/rive/drawable.hpp +++ b/include/rive/drawable.hpp @@ -12,6 +12,7 @@ namespace rive class ClippingShape; class Artboard; class DrawRules; +class LayoutComponent; class Drawable : public DrawableBase { @@ -28,7 +29,7 @@ class Drawable : public DrawableBase public: BlendMode blendMode() const { return (BlendMode)blendModeValue(); } - ClipResult clip(Renderer* renderer) const; + ClipResult applyClip(Renderer* renderer) const; virtual void draw(Renderer* renderer) = 0; virtual Core* hitTest(HitInfo*, const Mat2D&) = 0; void addClippingShape(ClippingShape* shape); @@ -46,6 +47,29 @@ class Drawable : public DrawableBase return (static_cast(drawableFlags()) & DrawableFlag::Opaque) == DrawableFlag::Opaque; } + + bool isChildOfLayout(LayoutComponent* layout); + + StatusCode onAddedDirty(CoreContext* context) override; +}; + +class ProxyDrawing +{ +public: + virtual void drawProxy(Renderer* renderer) = 0; +}; + +class DrawableProxy : public Drawable +{ +private: + ProxyDrawing* m_proxyDrawing; + +public: + DrawableProxy(ProxyDrawing* proxy) : m_proxyDrawing(proxy) {} + + void draw(Renderer* renderer) override { m_proxyDrawing->drawProxy(renderer); } + + Core* hitTest(HitInfo*, const Mat2D&) override { return nullptr; } }; } // namespace rive diff --git a/include/rive/drawable_flag.hpp b/include/rive/drawable_flag.hpp index 9f79a152..c21604db 100644 --- a/include/rive/drawable_flag.hpp +++ b/include/rive/drawable_flag.hpp @@ -20,6 +20,10 @@ enum class DrawableFlag : unsigned short /// Whether this Component lets hit events pass through to components behind it Opaque = 1 << 3, + + /// Whether the computed world bounds for a shape need to be recalculated + /// Using Clean instead of dirty so it doesn't need to be initialized to 1 + WorldBoundsClean = 1 << 4, }; RIVE_MAKE_ENUM_BITSET(DrawableFlag) } // namespace rive diff --git a/include/rive/event_report.hpp b/include/rive/event_report.hpp new file mode 100644 index 00000000..fe56315e --- /dev/null +++ b/include/rive/event_report.hpp @@ -0,0 +1,21 @@ +#ifndef _RIVE_EVENT_REPORT_HPP_ +#define _RIVE_EVENT_REPORT_HPP_ + +namespace rive +{ +class Event; + +class EventReport +{ +public: + EventReport(Event* event, float secondsDelay) : m_event(event), m_secondsDelay(secondsDelay) {} + Event* event() const { return m_event; } + float secondsDelay() const { return m_secondsDelay; } + +private: + Event* m_event; + float m_secondsDelay; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/factory.hpp b/include/rive/factory.hpp index 1484c0fd..417d4efc 100644 --- a/include/rive/factory.hpp +++ b/include/rive/factory.hpp @@ -7,6 +7,7 @@ #include "rive/renderer.hpp" #include "rive/text_engine.hpp" +#include "rive/audio/audio_source.hpp" #include "rive/refcnt.hpp" #include "rive/span.hpp" #include "rive/math/aabb.hpp" @@ -58,6 +59,8 @@ class Factory virtual rcp decodeFont(Span); + virtual rcp decodeAudio(Span); + // Non-virtual helpers rcp makeRenderPath(const AABB&); diff --git a/include/rive/file.hpp b/include/rive/file.hpp index 9f6659d9..52c1dfec 100644 --- a/include/rive/file.hpp +++ b/include/rive/file.hpp @@ -5,6 +5,12 @@ #include "rive/backboard.hpp" #include "rive/factory.hpp" #include "rive/file_asset_loader.hpp" +#include "rive/viewmodel/data_enum.hpp" +#include "rive/viewmodel/viewmodel_component.hpp" +#include "rive/viewmodel/viewmodel_instance.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" +#include "rive/viewmodel/viewmodel_instance_viewmodel.hpp" +#include "rive/viewmodel/viewmodel_instance_list_item.hpp" #include #include @@ -82,6 +88,23 @@ class File /// index is out of range. Artboard* artboard(size_t index) const; + /// @returns a view model instance of the view model with the specified name. + ViewModelInstance* createViewModelInstance(std::string name); + + /// @returns a view model instance attached to the artboard if it exists. + ViewModelInstance* createViewModelInstance(Artboard* artboard); + + /// @returns a view model instance of the viewModel. + ViewModelInstance* createViewModelInstance(ViewModel* viewModel); + + /// @returns a view model instance of the viewModel by name and instance name. + ViewModelInstance* createViewModelInstance(std::string name, std::string instanceName); + + ViewModel* viewModel(std::string name); + ViewModelInstanceListItem* viewModelInstanceListItem(ViewModelInstance* viewModelInstance); + ViewModelInstanceListItem* viewModelInstanceListItem(ViewModelInstance* viewModelInstance, + Artboard* artboard); + #ifdef WITH_RIVE_TOOLS /// Strips FileAssetContents for FileAssets of given typeKeys. /// @param data the raw data of the file. @@ -107,11 +130,17 @@ class File /// Rive components and animations. std::vector m_artboards; + std::vector m_ViewModels; + std::vector m_Enums; + Factory* m_factory; /// The helper used to load assets when they're not provided in-band /// with the file. FileAssetLoader* m_assetLoader; + + void completeViewModelInstance(ViewModelInstance* viewModelInstance); + ViewModelInstance* copyViewModelInstance(ViewModelInstance* viewModelInstance); }; } // namespace rive #endif diff --git a/include/rive/generated/animation/keyframe_uint_base.hpp b/include/rive/generated/animation/keyframe_uint_base.hpp new file mode 100644 index 00000000..cdf0bd13 --- /dev/null +++ b/include/rive/generated/animation/keyframe_uint_base.hpp @@ -0,0 +1,72 @@ +#ifndef _RIVE_KEY_FRAME_UINT_BASE_HPP_ +#define _RIVE_KEY_FRAME_UINT_BASE_HPP_ +#include "rive/animation/interpolating_keyframe.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class KeyFrameUintBase : public InterpolatingKeyFrame +{ +protected: + typedef InterpolatingKeyFrame Super; + +public: + static const uint16_t typeKey = 450; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case KeyFrameUintBase::typeKey: + case InterpolatingKeyFrameBase::typeKey: + case KeyFrameBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t valuePropertyKey = 631; + +private: + uint32_t m_Value = 0; + +public: + inline uint32_t value() const { return m_Value; } + void value(uint32_t value) + { + if (m_Value == value) + { + return; + } + m_Value = value; + valueChanged(); + } + + Core* clone() const override; + void copy(const KeyFrameUintBase& object) + { + m_Value = object.m_Value; + InterpolatingKeyFrame::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case valuePropertyKey: + m_Value = CoreUintType::deserialize(reader); + return true; + } + return InterpolatingKeyFrame::deserialize(propertyKey, reader); + } + +protected: + virtual void valueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/animation/layer_state_base.hpp b/include/rive/generated/animation/layer_state_base.hpp index a14df6c0..3c4cfbe9 100644 --- a/include/rive/generated/animation/layer_state_base.hpp +++ b/include/rive/generated/animation/layer_state_base.hpp @@ -1,6 +1,7 @@ #ifndef _RIVE_LAYER_STATE_BASE_HPP_ #define _RIVE_LAYER_STATE_BASE_HPP_ #include "rive/animation/state_machine_layer_component.hpp" +#include "rive/core/field_types/core_uint_type.hpp" namespace rive { class LayerStateBase : public StateMachineLayerComponent @@ -27,7 +28,42 @@ class LayerStateBase : public StateMachineLayerComponent uint16_t coreType() const override { return typeKey; } + static const uint16_t flagsPropertyKey = 536; + +private: + uint32_t m_Flags = 0; + +public: + inline uint32_t flags() const { return m_Flags; } + void flags(uint32_t value) + { + if (m_Flags == value) + { + return; + } + m_Flags = value; + flagsChanged(); + } + + void copy(const LayerStateBase& object) + { + m_Flags = object.m_Flags; + StateMachineLayerComponent::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case flagsPropertyKey: + m_Flags = CoreUintType::deserialize(reader); + return true; + } + return StateMachineLayerComponent::deserialize(propertyKey, reader); + } + protected: + virtual void flagsChanged() {} }; } // namespace rive diff --git a/include/rive/generated/animation/listener_align_target_base.hpp b/include/rive/generated/animation/listener_align_target_base.hpp index 79c0cfc4..b7e5800f 100644 --- a/include/rive/generated/animation/listener_align_target_base.hpp +++ b/include/rive/generated/animation/listener_align_target_base.hpp @@ -1,6 +1,7 @@ #ifndef _RIVE_LISTENER_ALIGN_TARGET_BASE_HPP_ #define _RIVE_LISTENER_ALIGN_TARGET_BASE_HPP_ #include "rive/animation/listener_action.hpp" +#include "rive/core/field_types/core_bool_type.hpp" #include "rive/core/field_types/core_uint_type.hpp" namespace rive { @@ -29,9 +30,11 @@ class ListenerAlignTargetBase : public ListenerAction uint16_t coreType() const override { return typeKey; } static const uint16_t targetIdPropertyKey = 240; + static const uint16_t preserveOffsetPropertyKey = 541; private: uint32_t m_TargetId = 0; + bool m_PreserveOffset = false; public: inline uint32_t targetId() const { return m_TargetId; } @@ -45,10 +48,22 @@ class ListenerAlignTargetBase : public ListenerAction targetIdChanged(); } + inline bool preserveOffset() const { return m_PreserveOffset; } + void preserveOffset(bool value) + { + if (m_PreserveOffset == value) + { + return; + } + m_PreserveOffset = value; + preserveOffsetChanged(); + } + Core* clone() const override; void copy(const ListenerAlignTargetBase& object) { m_TargetId = object.m_TargetId; + m_PreserveOffset = object.m_PreserveOffset; ListenerAction::copy(object); } @@ -59,12 +74,16 @@ class ListenerAlignTargetBase : public ListenerAction case targetIdPropertyKey: m_TargetId = CoreUintType::deserialize(reader); return true; + case preserveOffsetPropertyKey: + m_PreserveOffset = CoreBoolType::deserialize(reader); + return true; } return ListenerAction::deserialize(propertyKey, reader); } protected: virtual void targetIdChanged() {} + virtual void preserveOffsetChanged() {} }; } // namespace rive diff --git a/include/rive/generated/animation/listener_viewmodel_change_base.hpp b/include/rive/generated/animation/listener_viewmodel_change_base.hpp new file mode 100644 index 00000000..4171141c --- /dev/null +++ b/include/rive/generated/animation/listener_viewmodel_change_base.hpp @@ -0,0 +1,36 @@ +#ifndef _RIVE_LISTENER_VIEW_MODEL_CHANGE_BASE_HPP_ +#define _RIVE_LISTENER_VIEW_MODEL_CHANGE_BASE_HPP_ +#include "rive/animation/listener_action.hpp" +namespace rive +{ +class ListenerViewModelChangeBase : public ListenerAction +{ +protected: + typedef ListenerAction Super; + +public: + static const uint16_t typeKey = 487; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ListenerViewModelChangeBase::typeKey: + case ListenerActionBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/animation/nested_bool_base.hpp b/include/rive/generated/animation/nested_bool_base.hpp index d756d955..8c015f5a 100644 --- a/include/rive/generated/animation/nested_bool_base.hpp +++ b/include/rive/generated/animation/nested_bool_base.hpp @@ -35,16 +35,8 @@ class NestedBoolBase : public NestedInput bool m_NestedValue = false; public: - inline bool nestedValue() const { return m_NestedValue; } - void nestedValue(bool value) - { - if (m_NestedValue == value) - { - return; - } - m_NestedValue = value; - nestedValueChanged(); - } + virtual bool nestedValue() const { return m_NestedValue; } + virtual void nestedValue(bool value) = 0; Core* clone() const override; void copy(const NestedBoolBase& object) diff --git a/include/rive/generated/animation/nested_number_base.hpp b/include/rive/generated/animation/nested_number_base.hpp index e50ecacb..82bc0c94 100644 --- a/include/rive/generated/animation/nested_number_base.hpp +++ b/include/rive/generated/animation/nested_number_base.hpp @@ -35,16 +35,8 @@ class NestedNumberBase : public NestedInput float m_NestedValue = 0.0f; public: - inline float nestedValue() const { return m_NestedValue; } - void nestedValue(float value) - { - if (m_NestedValue == value) - { - return; - } - m_NestedValue = value; - nestedValueChanged(); - } + virtual float nestedValue() const { return m_NestedValue; } + virtual void nestedValue(float value) = 0; Core* clone() const override; void copy(const NestedNumberBase& object) diff --git a/include/rive/generated/animation/state_transition_base.hpp b/include/rive/generated/animation/state_transition_base.hpp index 9b526ac0..7de3c369 100644 --- a/include/rive/generated/animation/state_transition_base.hpp +++ b/include/rive/generated/animation/state_transition_base.hpp @@ -34,6 +34,7 @@ class StateTransitionBase : public StateMachineLayerComponent static const uint16_t exitTimePropertyKey = 160; static const uint16_t interpolationTypePropertyKey = 349; static const uint16_t interpolatorIdPropertyKey = 350; + static const uint16_t randomWeightPropertyKey = 537; private: uint32_t m_StateToId = -1; @@ -42,6 +43,7 @@ class StateTransitionBase : public StateMachineLayerComponent uint32_t m_ExitTime = 0; uint32_t m_InterpolationType = 1; uint32_t m_InterpolatorId = -1; + uint32_t m_RandomWeight = 1; public: inline uint32_t stateToId() const { return m_StateToId; } @@ -110,6 +112,17 @@ class StateTransitionBase : public StateMachineLayerComponent interpolatorIdChanged(); } + inline uint32_t randomWeight() const { return m_RandomWeight; } + void randomWeight(uint32_t value) + { + if (m_RandomWeight == value) + { + return; + } + m_RandomWeight = value; + randomWeightChanged(); + } + Core* clone() const override; void copy(const StateTransitionBase& object) { @@ -119,6 +132,7 @@ class StateTransitionBase : public StateMachineLayerComponent m_ExitTime = object.m_ExitTime; m_InterpolationType = object.m_InterpolationType; m_InterpolatorId = object.m_InterpolatorId; + m_RandomWeight = object.m_RandomWeight; StateMachineLayerComponent::copy(object); } @@ -144,6 +158,9 @@ class StateTransitionBase : public StateMachineLayerComponent case interpolatorIdPropertyKey: m_InterpolatorId = CoreUintType::deserialize(reader); return true; + case randomWeightPropertyKey: + m_RandomWeight = CoreUintType::deserialize(reader); + return true; } return StateMachineLayerComponent::deserialize(propertyKey, reader); } @@ -155,6 +172,7 @@ class StateTransitionBase : public StateMachineLayerComponent virtual void exitTimeChanged() {} virtual void interpolationTypeChanged() {} virtual void interpolatorIdChanged() {} + virtual void randomWeightChanged() {} }; } // namespace rive diff --git a/include/rive/generated/animation/transition_artboard_condition_base.hpp b/include/rive/generated/animation/transition_artboard_condition_base.hpp new file mode 100644 index 00000000..5f079c44 --- /dev/null +++ b/include/rive/generated/animation/transition_artboard_condition_base.hpp @@ -0,0 +1,37 @@ +#ifndef _RIVE_TRANSITION_ARTBOARD_CONDITION_BASE_HPP_ +#define _RIVE_TRANSITION_ARTBOARD_CONDITION_BASE_HPP_ +#include "rive/animation/transition_viewmodel_condition.hpp" +namespace rive +{ +class TransitionArtboardConditionBase : public TransitionViewModelCondition +{ +protected: + typedef TransitionViewModelCondition Super; + +public: + static const uint16_t typeKey = 497; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TransitionArtboardConditionBase::typeKey: + case TransitionViewModelConditionBase::typeKey: + case TransitionConditionBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/animation/transition_bool_condition_base.hpp b/include/rive/generated/animation/transition_bool_condition_base.hpp index 8567f376..c9783eab 100644 --- a/include/rive/generated/animation/transition_bool_condition_base.hpp +++ b/include/rive/generated/animation/transition_bool_condition_base.hpp @@ -19,6 +19,7 @@ class TransitionBoolConditionBase : public TransitionValueCondition { case TransitionBoolConditionBase::typeKey: case TransitionValueConditionBase::typeKey: + case TransitionInputConditionBase::typeKey: case TransitionConditionBase::typeKey: return true; default: diff --git a/include/rive/generated/animation/transition_comparator_base.hpp b/include/rive/generated/animation/transition_comparator_base.hpp new file mode 100644 index 00000000..7b66f88f --- /dev/null +++ b/include/rive/generated/animation/transition_comparator_base.hpp @@ -0,0 +1,37 @@ +#ifndef _RIVE_TRANSITION_COMPARATOR_BASE_HPP_ +#define _RIVE_TRANSITION_COMPARATOR_BASE_HPP_ +#include "rive/core.hpp" +namespace rive +{ +class TransitionComparatorBase : public Core +{ +protected: + typedef Core Super; + +public: + static const uint16_t typeKey = 477; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TransitionComparatorBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + void copy(const TransitionComparatorBase& object) {} + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override { return false; } + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/animation/transition_condition_base.hpp b/include/rive/generated/animation/transition_condition_base.hpp index e0ec5daa..23145d54 100644 --- a/include/rive/generated/animation/transition_condition_base.hpp +++ b/include/rive/generated/animation/transition_condition_base.hpp @@ -1,7 +1,6 @@ #ifndef _RIVE_TRANSITION_CONDITION_BASE_HPP_ #define _RIVE_TRANSITION_CONDITION_BASE_HPP_ #include "rive/core.hpp" -#include "rive/core/field_types/core_uint_type.hpp" namespace rive { class TransitionConditionBase : public Core @@ -10,7 +9,7 @@ class TransitionConditionBase : public Core typedef Core Super; public: - static const uint16_t typeKey = 67; + static const uint16_t typeKey = 476; /// Helper to quickly determine if a core object extends another without RTTI /// at runtime. @@ -27,38 +26,11 @@ class TransitionConditionBase : public Core uint16_t coreType() const override { return typeKey; } - static const uint16_t inputIdPropertyKey = 155; + void copy(const TransitionConditionBase& object) {} -private: - uint32_t m_InputId = -1; - -public: - inline uint32_t inputId() const { return m_InputId; } - void inputId(uint32_t value) - { - if (m_InputId == value) - { - return; - } - m_InputId = value; - inputIdChanged(); - } - - void copy(const TransitionConditionBase& object) { m_InputId = object.m_InputId; } - - bool deserialize(uint16_t propertyKey, BinaryReader& reader) override - { - switch (propertyKey) - { - case inputIdPropertyKey: - m_InputId = CoreUintType::deserialize(reader); - return true; - } - return false; - } + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override { return false; } protected: - virtual void inputIdChanged() {} }; } // namespace rive diff --git a/include/rive/generated/animation/transition_input_condition_base.hpp b/include/rive/generated/animation/transition_input_condition_base.hpp new file mode 100644 index 00000000..1440bf7b --- /dev/null +++ b/include/rive/generated/animation/transition_input_condition_base.hpp @@ -0,0 +1,70 @@ +#ifndef _RIVE_TRANSITION_INPUT_CONDITION_BASE_HPP_ +#define _RIVE_TRANSITION_INPUT_CONDITION_BASE_HPP_ +#include "rive/animation/transition_condition.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class TransitionInputConditionBase : public TransitionCondition +{ +protected: + typedef TransitionCondition Super; + +public: + static const uint16_t typeKey = 67; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TransitionInputConditionBase::typeKey: + case TransitionConditionBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t inputIdPropertyKey = 155; + +private: + uint32_t m_InputId = -1; + +public: + inline uint32_t inputId() const { return m_InputId; } + void inputId(uint32_t value) + { + if (m_InputId == value) + { + return; + } + m_InputId = value; + inputIdChanged(); + } + + void copy(const TransitionInputConditionBase& object) + { + m_InputId = object.m_InputId; + TransitionCondition::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case inputIdPropertyKey: + m_InputId = CoreUintType::deserialize(reader); + return true; + } + return TransitionCondition::deserialize(propertyKey, reader); + } + +protected: + virtual void inputIdChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/animation/transition_number_condition_base.hpp b/include/rive/generated/animation/transition_number_condition_base.hpp index 5d4cc796..82e1a81b 100644 --- a/include/rive/generated/animation/transition_number_condition_base.hpp +++ b/include/rive/generated/animation/transition_number_condition_base.hpp @@ -20,6 +20,7 @@ class TransitionNumberConditionBase : public TransitionValueCondition { case TransitionNumberConditionBase::typeKey: case TransitionValueConditionBase::typeKey: + case TransitionInputConditionBase::typeKey: case TransitionConditionBase::typeKey: return true; default: diff --git a/include/rive/generated/animation/transition_property_artboard_comparator_base.hpp b/include/rive/generated/animation/transition_property_artboard_comparator_base.hpp new file mode 100644 index 00000000..354087a9 --- /dev/null +++ b/include/rive/generated/animation/transition_property_artboard_comparator_base.hpp @@ -0,0 +1,72 @@ +#ifndef _RIVE_TRANSITION_PROPERTY_ARTBOARD_COMPARATOR_BASE_HPP_ +#define _RIVE_TRANSITION_PROPERTY_ARTBOARD_COMPARATOR_BASE_HPP_ +#include "rive/animation/transition_property_comparator.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class TransitionPropertyArtboardComparatorBase : public TransitionPropertyComparator +{ +protected: + typedef TransitionPropertyComparator Super; + +public: + static const uint16_t typeKey = 496; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TransitionPropertyArtboardComparatorBase::typeKey: + case TransitionPropertyComparatorBase::typeKey: + case TransitionComparatorBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t propertyTypePropertyKey = 677; + +private: + uint32_t m_PropertyType = 0; + +public: + inline uint32_t propertyType() const { return m_PropertyType; } + void propertyType(uint32_t value) + { + if (m_PropertyType == value) + { + return; + } + m_PropertyType = value; + propertyTypeChanged(); + } + + Core* clone() const override; + void copy(const TransitionPropertyArtboardComparatorBase& object) + { + m_PropertyType = object.m_PropertyType; + TransitionPropertyComparator::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case propertyTypePropertyKey: + m_PropertyType = CoreUintType::deserialize(reader); + return true; + } + return TransitionPropertyComparator::deserialize(propertyKey, reader); + } + +protected: + virtual void propertyTypeChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/animation/transition_property_comparator_base.hpp b/include/rive/generated/animation/transition_property_comparator_base.hpp new file mode 100644 index 00000000..33497c27 --- /dev/null +++ b/include/rive/generated/animation/transition_property_comparator_base.hpp @@ -0,0 +1,34 @@ +#ifndef _RIVE_TRANSITION_PROPERTY_COMPARATOR_BASE_HPP_ +#define _RIVE_TRANSITION_PROPERTY_COMPARATOR_BASE_HPP_ +#include "rive/animation/transition_comparator.hpp" +namespace rive +{ +class TransitionPropertyComparatorBase : public TransitionComparator +{ +protected: + typedef TransitionComparator Super; + +public: + static const uint16_t typeKey = 478; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TransitionPropertyComparatorBase::typeKey: + case TransitionComparatorBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/animation/transition_property_viewmodel_comparator_base.hpp b/include/rive/generated/animation/transition_property_viewmodel_comparator_base.hpp new file mode 100644 index 00000000..d7d2168b --- /dev/null +++ b/include/rive/generated/animation/transition_property_viewmodel_comparator_base.hpp @@ -0,0 +1,37 @@ +#ifndef _RIVE_TRANSITION_PROPERTY_VIEW_MODEL_COMPARATOR_BASE_HPP_ +#define _RIVE_TRANSITION_PROPERTY_VIEW_MODEL_COMPARATOR_BASE_HPP_ +#include "rive/animation/transition_property_comparator.hpp" +namespace rive +{ +class TransitionPropertyViewModelComparatorBase : public TransitionPropertyComparator +{ +protected: + typedef TransitionPropertyComparator Super; + +public: + static const uint16_t typeKey = 479; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TransitionPropertyViewModelComparatorBase::typeKey: + case TransitionPropertyComparatorBase::typeKey: + case TransitionComparatorBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/animation/transition_trigger_condition_base.hpp b/include/rive/generated/animation/transition_trigger_condition_base.hpp index b10c5590..902f1a42 100644 --- a/include/rive/generated/animation/transition_trigger_condition_base.hpp +++ b/include/rive/generated/animation/transition_trigger_condition_base.hpp @@ -1,12 +1,12 @@ #ifndef _RIVE_TRANSITION_TRIGGER_CONDITION_BASE_HPP_ #define _RIVE_TRANSITION_TRIGGER_CONDITION_BASE_HPP_ -#include "rive/animation/transition_condition.hpp" +#include "rive/animation/transition_input_condition.hpp" namespace rive { -class TransitionTriggerConditionBase : public TransitionCondition +class TransitionTriggerConditionBase : public TransitionInputCondition { protected: - typedef TransitionCondition Super; + typedef TransitionInputCondition Super; public: static const uint16_t typeKey = 68; @@ -18,6 +18,7 @@ class TransitionTriggerConditionBase : public TransitionCondition switch (typeKey) { case TransitionTriggerConditionBase::typeKey: + case TransitionInputConditionBase::typeKey: case TransitionConditionBase::typeKey: return true; default: diff --git a/include/rive/generated/animation/transition_value_boolean_comparator_base.hpp b/include/rive/generated/animation/transition_value_boolean_comparator_base.hpp new file mode 100644 index 00000000..bdbcf289 --- /dev/null +++ b/include/rive/generated/animation/transition_value_boolean_comparator_base.hpp @@ -0,0 +1,72 @@ +#ifndef _RIVE_TRANSITION_VALUE_BOOLEAN_COMPARATOR_BASE_HPP_ +#define _RIVE_TRANSITION_VALUE_BOOLEAN_COMPARATOR_BASE_HPP_ +#include "rive/animation/transition_value_comparator.hpp" +#include "rive/core/field_types/core_bool_type.hpp" +namespace rive +{ +class TransitionValueBooleanComparatorBase : public TransitionValueComparator +{ +protected: + typedef TransitionValueComparator Super; + +public: + static const uint16_t typeKey = 481; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TransitionValueBooleanComparatorBase::typeKey: + case TransitionValueComparatorBase::typeKey: + case TransitionComparatorBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t valuePropertyKey = 647; + +private: + bool m_Value = false; + +public: + inline bool value() const { return m_Value; } + void value(bool value) + { + if (m_Value == value) + { + return; + } + m_Value = value; + valueChanged(); + } + + Core* clone() const override; + void copy(const TransitionValueBooleanComparatorBase& object) + { + m_Value = object.m_Value; + TransitionValueComparator::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case valuePropertyKey: + m_Value = CoreBoolType::deserialize(reader); + return true; + } + return TransitionValueComparator::deserialize(propertyKey, reader); + } + +protected: + virtual void valueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/animation/transition_value_color_comparator_base.hpp b/include/rive/generated/animation/transition_value_color_comparator_base.hpp new file mode 100644 index 00000000..f44d2be8 --- /dev/null +++ b/include/rive/generated/animation/transition_value_color_comparator_base.hpp @@ -0,0 +1,72 @@ +#ifndef _RIVE_TRANSITION_VALUE_COLOR_COMPARATOR_BASE_HPP_ +#define _RIVE_TRANSITION_VALUE_COLOR_COMPARATOR_BASE_HPP_ +#include "rive/animation/transition_value_comparator.hpp" +#include "rive/core/field_types/core_color_type.hpp" +namespace rive +{ +class TransitionValueColorComparatorBase : public TransitionValueComparator +{ +protected: + typedef TransitionValueComparator Super; + +public: + static const uint16_t typeKey = 483; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TransitionValueColorComparatorBase::typeKey: + case TransitionValueComparatorBase::typeKey: + case TransitionComparatorBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t valuePropertyKey = 651; + +private: + int m_Value = 0xFF1D1D1D; + +public: + inline int value() const { return m_Value; } + void value(int value) + { + if (m_Value == value) + { + return; + } + m_Value = value; + valueChanged(); + } + + Core* clone() const override; + void copy(const TransitionValueColorComparatorBase& object) + { + m_Value = object.m_Value; + TransitionValueComparator::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case valuePropertyKey: + m_Value = CoreColorType::deserialize(reader); + return true; + } + return TransitionValueComparator::deserialize(propertyKey, reader); + } + +protected: + virtual void valueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/animation/transition_value_comparator_base.hpp b/include/rive/generated/animation/transition_value_comparator_base.hpp new file mode 100644 index 00000000..aced3729 --- /dev/null +++ b/include/rive/generated/animation/transition_value_comparator_base.hpp @@ -0,0 +1,34 @@ +#ifndef _RIVE_TRANSITION_VALUE_COMPARATOR_BASE_HPP_ +#define _RIVE_TRANSITION_VALUE_COMPARATOR_BASE_HPP_ +#include "rive/animation/transition_comparator.hpp" +namespace rive +{ +class TransitionValueComparatorBase : public TransitionComparator +{ +protected: + typedef TransitionComparator Super; + +public: + static const uint16_t typeKey = 480; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TransitionValueComparatorBase::typeKey: + case TransitionComparatorBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/animation/transition_value_condition_base.hpp b/include/rive/generated/animation/transition_value_condition_base.hpp index 9fe2c464..e9c680e8 100644 --- a/include/rive/generated/animation/transition_value_condition_base.hpp +++ b/include/rive/generated/animation/transition_value_condition_base.hpp @@ -1,13 +1,13 @@ #ifndef _RIVE_TRANSITION_VALUE_CONDITION_BASE_HPP_ #define _RIVE_TRANSITION_VALUE_CONDITION_BASE_HPP_ -#include "rive/animation/transition_condition.hpp" +#include "rive/animation/transition_input_condition.hpp" #include "rive/core/field_types/core_uint_type.hpp" namespace rive { -class TransitionValueConditionBase : public TransitionCondition +class TransitionValueConditionBase : public TransitionInputCondition { protected: - typedef TransitionCondition Super; + typedef TransitionInputCondition Super; public: static const uint16_t typeKey = 69; @@ -19,6 +19,7 @@ class TransitionValueConditionBase : public TransitionCondition switch (typeKey) { case TransitionValueConditionBase::typeKey: + case TransitionInputConditionBase::typeKey: case TransitionConditionBase::typeKey: return true; default: @@ -48,7 +49,7 @@ class TransitionValueConditionBase : public TransitionCondition void copy(const TransitionValueConditionBase& object) { m_OpValue = object.m_OpValue; - TransitionCondition::copy(object); + TransitionInputCondition::copy(object); } bool deserialize(uint16_t propertyKey, BinaryReader& reader) override @@ -59,7 +60,7 @@ class TransitionValueConditionBase : public TransitionCondition m_OpValue = CoreUintType::deserialize(reader); return true; } - return TransitionCondition::deserialize(propertyKey, reader); + return TransitionInputCondition::deserialize(propertyKey, reader); } protected: diff --git a/include/rive/generated/animation/transition_value_enum_comparator_base.hpp b/include/rive/generated/animation/transition_value_enum_comparator_base.hpp new file mode 100644 index 00000000..ec9bb455 --- /dev/null +++ b/include/rive/generated/animation/transition_value_enum_comparator_base.hpp @@ -0,0 +1,72 @@ +#ifndef _RIVE_TRANSITION_VALUE_ENUM_COMPARATOR_BASE_HPP_ +#define _RIVE_TRANSITION_VALUE_ENUM_COMPARATOR_BASE_HPP_ +#include "rive/animation/transition_value_comparator.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class TransitionValueEnumComparatorBase : public TransitionValueComparator +{ +protected: + typedef TransitionValueComparator Super; + +public: + static const uint16_t typeKey = 485; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TransitionValueEnumComparatorBase::typeKey: + case TransitionValueComparatorBase::typeKey: + case TransitionComparatorBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t valuePropertyKey = 653; + +private: + uint32_t m_Value = -1; + +public: + inline uint32_t value() const { return m_Value; } + void value(uint32_t value) + { + if (m_Value == value) + { + return; + } + m_Value = value; + valueChanged(); + } + + Core* clone() const override; + void copy(const TransitionValueEnumComparatorBase& object) + { + m_Value = object.m_Value; + TransitionValueComparator::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case valuePropertyKey: + m_Value = CoreUintType::deserialize(reader); + return true; + } + return TransitionValueComparator::deserialize(propertyKey, reader); + } + +protected: + virtual void valueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/animation/transition_value_number_comparator_base.hpp b/include/rive/generated/animation/transition_value_number_comparator_base.hpp new file mode 100644 index 00000000..7fc7e7cd --- /dev/null +++ b/include/rive/generated/animation/transition_value_number_comparator_base.hpp @@ -0,0 +1,72 @@ +#ifndef _RIVE_TRANSITION_VALUE_NUMBER_COMPARATOR_BASE_HPP_ +#define _RIVE_TRANSITION_VALUE_NUMBER_COMPARATOR_BASE_HPP_ +#include "rive/animation/transition_value_comparator.hpp" +#include "rive/core/field_types/core_double_type.hpp" +namespace rive +{ +class TransitionValueNumberComparatorBase : public TransitionValueComparator +{ +protected: + typedef TransitionValueComparator Super; + +public: + static const uint16_t typeKey = 484; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TransitionValueNumberComparatorBase::typeKey: + case TransitionValueComparatorBase::typeKey: + case TransitionComparatorBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t valuePropertyKey = 652; + +private: + float m_Value = 0.0f; + +public: + inline float value() const { return m_Value; } + void value(float value) + { + if (m_Value == value) + { + return; + } + m_Value = value; + valueChanged(); + } + + Core* clone() const override; + void copy(const TransitionValueNumberComparatorBase& object) + { + m_Value = object.m_Value; + TransitionValueComparator::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case valuePropertyKey: + m_Value = CoreDoubleType::deserialize(reader); + return true; + } + return TransitionValueComparator::deserialize(propertyKey, reader); + } + +protected: + virtual void valueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/animation/transition_value_string_comparator_base.hpp b/include/rive/generated/animation/transition_value_string_comparator_base.hpp new file mode 100644 index 00000000..ecd1740c --- /dev/null +++ b/include/rive/generated/animation/transition_value_string_comparator_base.hpp @@ -0,0 +1,73 @@ +#ifndef _RIVE_TRANSITION_VALUE_STRING_COMPARATOR_BASE_HPP_ +#define _RIVE_TRANSITION_VALUE_STRING_COMPARATOR_BASE_HPP_ +#include +#include "rive/animation/transition_value_comparator.hpp" +#include "rive/core/field_types/core_string_type.hpp" +namespace rive +{ +class TransitionValueStringComparatorBase : public TransitionValueComparator +{ +protected: + typedef TransitionValueComparator Super; + +public: + static const uint16_t typeKey = 486; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TransitionValueStringComparatorBase::typeKey: + case TransitionValueComparatorBase::typeKey: + case TransitionComparatorBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t valuePropertyKey = 654; + +private: + std::string m_Value = ""; + +public: + inline const std::string& value() const { return m_Value; } + void value(std::string value) + { + if (m_Value == value) + { + return; + } + m_Value = value; + valueChanged(); + } + + Core* clone() const override; + void copy(const TransitionValueStringComparatorBase& object) + { + m_Value = object.m_Value; + TransitionValueComparator::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case valuePropertyKey: + m_Value = CoreStringType::deserialize(reader); + return true; + } + return TransitionValueComparator::deserialize(propertyKey, reader); + } + +protected: + virtual void valueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/animation/transition_viewmodel_condition_base.hpp b/include/rive/generated/animation/transition_viewmodel_condition_base.hpp new file mode 100644 index 00000000..7c156a47 --- /dev/null +++ b/include/rive/generated/animation/transition_viewmodel_condition_base.hpp @@ -0,0 +1,107 @@ +#ifndef _RIVE_TRANSITION_VIEW_MODEL_CONDITION_BASE_HPP_ +#define _RIVE_TRANSITION_VIEW_MODEL_CONDITION_BASE_HPP_ +#include "rive/animation/transition_condition.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class TransitionViewModelConditionBase : public TransitionCondition +{ +protected: + typedef TransitionCondition Super; + +public: + static const uint16_t typeKey = 482; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case TransitionViewModelConditionBase::typeKey: + case TransitionConditionBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t leftComparatorIdPropertyKey = 648; + static const uint16_t rightComparatorIdPropertyKey = 649; + static const uint16_t opValuePropertyKey = 650; + +private: + uint32_t m_LeftComparatorId = -1; + uint32_t m_RightComparatorId = -1; + uint32_t m_OpValue = 0; + +public: + inline uint32_t leftComparatorId() const { return m_LeftComparatorId; } + void leftComparatorId(uint32_t value) + { + if (m_LeftComparatorId == value) + { + return; + } + m_LeftComparatorId = value; + leftComparatorIdChanged(); + } + + inline uint32_t rightComparatorId() const { return m_RightComparatorId; } + void rightComparatorId(uint32_t value) + { + if (m_RightComparatorId == value) + { + return; + } + m_RightComparatorId = value; + rightComparatorIdChanged(); + } + + inline uint32_t opValue() const { return m_OpValue; } + void opValue(uint32_t value) + { + if (m_OpValue == value) + { + return; + } + m_OpValue = value; + opValueChanged(); + } + + Core* clone() const override; + void copy(const TransitionViewModelConditionBase& object) + { + m_LeftComparatorId = object.m_LeftComparatorId; + m_RightComparatorId = object.m_RightComparatorId; + m_OpValue = object.m_OpValue; + TransitionCondition::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case leftComparatorIdPropertyKey: + m_LeftComparatorId = CoreUintType::deserialize(reader); + return true; + case rightComparatorIdPropertyKey: + m_RightComparatorId = CoreUintType::deserialize(reader); + return true; + case opValuePropertyKey: + m_OpValue = CoreUintType::deserialize(reader); + return true; + } + return TransitionCondition::deserialize(propertyKey, reader); + } + +protected: + virtual void leftComparatorIdChanged() {} + virtual void rightComparatorIdChanged() {} + virtual void opValueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/artboard_base.hpp b/include/rive/generated/artboard_base.hpp index feac51fb..b7ae1b94 100644 --- a/include/rive/generated/artboard_base.hpp +++ b/include/rive/generated/artboard_base.hpp @@ -1,15 +1,14 @@ #ifndef _RIVE_ARTBOARD_BASE_HPP_ #define _RIVE_ARTBOARD_BASE_HPP_ -#include "rive/core/field_types/core_bool_type.hpp" #include "rive/core/field_types/core_double_type.hpp" #include "rive/core/field_types/core_uint_type.hpp" -#include "rive/world_transform_component.hpp" +#include "rive/layout_component.hpp" namespace rive { -class ArtboardBase : public WorldTransformComponent +class ArtboardBase : public LayoutComponent { protected: - typedef WorldTransformComponent Super; + typedef LayoutComponent Super; public: static const uint16_t typeKey = 1; @@ -21,6 +20,10 @@ class ArtboardBase : public WorldTransformComponent switch (typeKey) { case ArtboardBase::typeKey: + case LayoutComponentBase::typeKey: + case DrawableBase::typeKey: + case NodeBase::typeKey: + case TransformComponentBase::typeKey: case WorldTransformComponentBase::typeKey: case ContainerComponentBase::typeKey: case ComponentBase::typeKey: @@ -32,81 +35,18 @@ class ArtboardBase : public WorldTransformComponent uint16_t coreType() const override { return typeKey; } - static const uint16_t clipPropertyKey = 196; - static const uint16_t widthPropertyKey = 7; - static const uint16_t heightPropertyKey = 8; - static const uint16_t xPropertyKey = 9; - static const uint16_t yPropertyKey = 10; static const uint16_t originXPropertyKey = 11; static const uint16_t originYPropertyKey = 12; static const uint16_t defaultStateMachineIdPropertyKey = 236; + static const uint16_t viewModelIdPropertyKey = 583; private: - bool m_Clip = true; - float m_Width = 0.0f; - float m_Height = 0.0f; - float m_X = 0.0f; - float m_Y = 0.0f; float m_OriginX = 0.0f; float m_OriginY = 0.0f; uint32_t m_DefaultStateMachineId = -1; + uint32_t m_ViewModelId = -1; public: - inline bool clip() const { return m_Clip; } - void clip(bool value) - { - if (m_Clip == value) - { - return; - } - m_Clip = value; - clipChanged(); - } - - inline float width() const { return m_Width; } - void width(float value) - { - if (m_Width == value) - { - return; - } - m_Width = value; - widthChanged(); - } - - inline float height() const { return m_Height; } - void height(float value) - { - if (m_Height == value) - { - return; - } - m_Height = value; - heightChanged(); - } - - inline float x() const { return m_X; } - void x(float value) - { - if (m_X == value) - { - return; - } - m_X = value; - xChanged(); - } - - inline float y() const { return m_Y; } - void y(float value) - { - if (m_Y == value) - { - return; - } - m_Y = value; - yChanged(); - } - inline float originX() const { return m_OriginX; } void originX(float value) { @@ -140,39 +80,31 @@ class ArtboardBase : public WorldTransformComponent defaultStateMachineIdChanged(); } + inline uint32_t viewModelId() const { return m_ViewModelId; } + void viewModelId(uint32_t value) + { + if (m_ViewModelId == value) + { + return; + } + m_ViewModelId = value; + viewModelIdChanged(); + } + Core* clone() const override; void copy(const ArtboardBase& object) { - m_Clip = object.m_Clip; - m_Width = object.m_Width; - m_Height = object.m_Height; - m_X = object.m_X; - m_Y = object.m_Y; m_OriginX = object.m_OriginX; m_OriginY = object.m_OriginY; m_DefaultStateMachineId = object.m_DefaultStateMachineId; - WorldTransformComponent::copy(object); + m_ViewModelId = object.m_ViewModelId; + LayoutComponent::copy(object); } bool deserialize(uint16_t propertyKey, BinaryReader& reader) override { switch (propertyKey) { - case clipPropertyKey: - m_Clip = CoreBoolType::deserialize(reader); - return true; - case widthPropertyKey: - m_Width = CoreDoubleType::deserialize(reader); - return true; - case heightPropertyKey: - m_Height = CoreDoubleType::deserialize(reader); - return true; - case xPropertyKey: - m_X = CoreDoubleType::deserialize(reader); - return true; - case yPropertyKey: - m_Y = CoreDoubleType::deserialize(reader); - return true; case originXPropertyKey: m_OriginX = CoreDoubleType::deserialize(reader); return true; @@ -182,19 +114,18 @@ class ArtboardBase : public WorldTransformComponent case defaultStateMachineIdPropertyKey: m_DefaultStateMachineId = CoreUintType::deserialize(reader); return true; + case viewModelIdPropertyKey: + m_ViewModelId = CoreUintType::deserialize(reader); + return true; } - return WorldTransformComponent::deserialize(propertyKey, reader); + return LayoutComponent::deserialize(propertyKey, reader); } protected: - virtual void clipChanged() {} - virtual void widthChanged() {} - virtual void heightChanged() {} - virtual void xChanged() {} - virtual void yChanged() {} virtual void originXChanged() {} virtual void originYChanged() {} virtual void defaultStateMachineIdChanged() {} + virtual void viewModelIdChanged() {} }; } // namespace rive diff --git a/include/rive/generated/assets/audio_asset_base.hpp b/include/rive/generated/assets/audio_asset_base.hpp index 6af1e4e8..49f65788 100644 --- a/include/rive/generated/assets/audio_asset_base.hpp +++ b/include/rive/generated/assets/audio_asset_base.hpp @@ -1,12 +1,12 @@ #ifndef _RIVE_AUDIO_ASSET_BASE_HPP_ #define _RIVE_AUDIO_ASSET_BASE_HPP_ -#include "rive/assets/file_asset.hpp" +#include "rive/assets/export_audio.hpp" namespace rive { -class AudioAssetBase : public FileAsset +class AudioAssetBase : public ExportAudio { protected: - typedef FileAsset Super; + typedef ExportAudio Super; public: static const uint16_t typeKey = 406; @@ -18,6 +18,7 @@ class AudioAssetBase : public FileAsset switch (typeKey) { case AudioAssetBase::typeKey: + case ExportAudioBase::typeKey: case FileAssetBase::typeKey: case AssetBase::typeKey: return true; diff --git a/include/rive/generated/assets/export_audio_base.hpp b/include/rive/generated/assets/export_audio_base.hpp new file mode 100644 index 00000000..12cb58c4 --- /dev/null +++ b/include/rive/generated/assets/export_audio_base.hpp @@ -0,0 +1,71 @@ +#ifndef _RIVE_EXPORT_AUDIO_BASE_HPP_ +#define _RIVE_EXPORT_AUDIO_BASE_HPP_ +#include "rive/assets/file_asset.hpp" +#include "rive/core/field_types/core_double_type.hpp" +namespace rive +{ +class ExportAudioBase : public FileAsset +{ +protected: + typedef FileAsset Super; + +public: + static const uint16_t typeKey = 422; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ExportAudioBase::typeKey: + case FileAssetBase::typeKey: + case AssetBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t volumePropertyKey = 530; + +private: + float m_Volume = 1.0f; + +public: + inline float volume() const { return m_Volume; } + void volume(float value) + { + if (m_Volume == value) + { + return; + } + m_Volume = value; + volumeChanged(); + } + + void copy(const ExportAudioBase& object) + { + m_Volume = object.m_Volume; + FileAsset::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case volumePropertyKey: + m_Volume = CoreDoubleType::deserialize(reader); + return true; + } + return FileAsset::deserialize(propertyKey, reader); + } + +protected: + virtual void volumeChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp index e8fe1081..bb2d399d 100644 --- a/include/rive/generated/core_registry.hpp +++ b/include/rive/generated/core_registry.hpp @@ -29,6 +29,7 @@ #include "rive/animation/keyframe_id.hpp" #include "rive/animation/keyframe_interpolator.hpp" #include "rive/animation/keyframe_string.hpp" +#include "rive/animation/keyframe_uint.hpp" #include "rive/animation/layer_state.hpp" #include "rive/animation/linear_animation.hpp" #include "rive/animation/listener_action.hpp" @@ -38,6 +39,7 @@ #include "rive/animation/listener_input_change.hpp" #include "rive/animation/listener_number_change.hpp" #include "rive/animation/listener_trigger_change.hpp" +#include "rive/animation/listener_viewmodel_change.hpp" #include "rive/animation/nested_bool.hpp" #include "rive/animation/nested_input.hpp" #include "rive/animation/nested_linear_animation.hpp" @@ -57,15 +59,29 @@ #include "rive/animation/state_machine_number.hpp" #include "rive/animation/state_machine_trigger.hpp" #include "rive/animation/state_transition.hpp" +#include "rive/animation/transition_artboard_condition.hpp" #include "rive/animation/transition_bool_condition.hpp" +#include "rive/animation/transition_comparator.hpp" #include "rive/animation/transition_condition.hpp" +#include "rive/animation/transition_input_condition.hpp" #include "rive/animation/transition_number_condition.hpp" +#include "rive/animation/transition_property_artboard_comparator.hpp" +#include "rive/animation/transition_property_comparator.hpp" +#include "rive/animation/transition_property_viewmodel_comparator.hpp" #include "rive/animation/transition_trigger_condition.hpp" +#include "rive/animation/transition_value_boolean_comparator.hpp" +#include "rive/animation/transition_value_color_comparator.hpp" +#include "rive/animation/transition_value_comparator.hpp" #include "rive/animation/transition_value_condition.hpp" +#include "rive/animation/transition_value_enum_comparator.hpp" +#include "rive/animation/transition_value_number_comparator.hpp" +#include "rive/animation/transition_value_string_comparator.hpp" +#include "rive/animation/transition_viewmodel_condition.hpp" #include "rive/artboard.hpp" #include "rive/assets/asset.hpp" #include "rive/assets/audio_asset.hpp" #include "rive/assets/drawable_asset.hpp" +#include "rive/assets/export_audio.hpp" #include "rive/assets/file_asset.hpp" #include "rive/assets/file_asset_contents.hpp" #include "rive/assets/folder.hpp" @@ -98,13 +114,36 @@ #include "rive/custom_property_boolean.hpp" #include "rive/custom_property_number.hpp" #include "rive/custom_property_string.hpp" +#include "rive/data_bind/bindable_property.hpp" +#include "rive/data_bind/bindable_property_boolean.hpp" +#include "rive/data_bind/bindable_property_color.hpp" +#include "rive/data_bind/bindable_property_enum.hpp" +#include "rive/data_bind/bindable_property_number.hpp" +#include "rive/data_bind/bindable_property_string.hpp" +#include "rive/data_bind/converters/data_converter.hpp" +#include "rive/data_bind/converters/data_converter_group.hpp" +#include "rive/data_bind/converters/data_converter_group_item.hpp" +#include "rive/data_bind/converters/data_converter_operation.hpp" +#include "rive/data_bind/converters/data_converter_rounder.hpp" +#include "rive/data_bind/converters/data_converter_to_string.hpp" +#include "rive/data_bind/data_bind.hpp" +#include "rive/data_bind/data_bind_context.hpp" #include "rive/draw_rules.hpp" #include "rive/draw_target.hpp" #include "rive/drawable.hpp" #include "rive/event.hpp" #include "rive/joystick.hpp" +#include "rive/layout/axis.hpp" +#include "rive/layout/axis_x.hpp" +#include "rive/layout/axis_y.hpp" +#include "rive/layout/layout_component_style.hpp" +#include "rive/layout/n_slicer.hpp" +#include "rive/layout/n_slicer_tile_mode.hpp" +#include "rive/layout_component.hpp" #include "rive/nested_animation.hpp" #include "rive/nested_artboard.hpp" +#include "rive/nested_artboard_layout.hpp" +#include "rive/nested_artboard_leaf.hpp" #include "rive/node.hpp" #include "rive/open_url_event.hpp" #include "rive/shapes/clipping_shape.hpp" @@ -148,6 +187,28 @@ #include "rive/text/text_value_run.hpp" #include "rive/text/text_variation_modifier.hpp" #include "rive/transform_component.hpp" +#include "rive/viewmodel/data_enum.hpp" +#include "rive/viewmodel/data_enum_value.hpp" +#include "rive/viewmodel/viewmodel.hpp" +#include "rive/viewmodel/viewmodel_component.hpp" +#include "rive/viewmodel/viewmodel_instance.hpp" +#include "rive/viewmodel/viewmodel_instance_boolean.hpp" +#include "rive/viewmodel/viewmodel_instance_color.hpp" +#include "rive/viewmodel/viewmodel_instance_enum.hpp" +#include "rive/viewmodel/viewmodel_instance_list.hpp" +#include "rive/viewmodel/viewmodel_instance_list_item.hpp" +#include "rive/viewmodel/viewmodel_instance_number.hpp" +#include "rive/viewmodel/viewmodel_instance_string.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" +#include "rive/viewmodel/viewmodel_instance_viewmodel.hpp" +#include "rive/viewmodel/viewmodel_property.hpp" +#include "rive/viewmodel/viewmodel_property_boolean.hpp" +#include "rive/viewmodel/viewmodel_property_color.hpp" +#include "rive/viewmodel/viewmodel_property_enum.hpp" +#include "rive/viewmodel/viewmodel_property_list.hpp" +#include "rive/viewmodel/viewmodel_property_number.hpp" +#include "rive/viewmodel/viewmodel_property_string.hpp" +#include "rive/viewmodel/viewmodel_property_viewmodel.hpp" #include "rive/world_transform_component.hpp" namespace rive { @@ -158,6 +219,48 @@ class CoreRegistry { switch (typeKey) { + case ViewModelInstanceListItemBase::typeKey: + return new ViewModelInstanceListItem(); + case ViewModelInstanceColorBase::typeKey: + return new ViewModelInstanceColor(); + case ViewModelComponentBase::typeKey: + return new ViewModelComponent(); + case ViewModelPropertyBase::typeKey: + return new ViewModelProperty(); + case ViewModelPropertyNumberBase::typeKey: + return new ViewModelPropertyNumber(); + case ViewModelInstanceEnumBase::typeKey: + return new ViewModelInstanceEnum(); + case ViewModelInstanceStringBase::typeKey: + return new ViewModelInstanceString(); + case ViewModelPropertyListBase::typeKey: + return new ViewModelPropertyList(); + case ViewModelBase::typeKey: + return new ViewModel(); + case ViewModelPropertyViewModelBase::typeKey: + return new ViewModelPropertyViewModel(); + case ViewModelInstanceBase::typeKey: + return new ViewModelInstance(); + case ViewModelPropertyBooleanBase::typeKey: + return new ViewModelPropertyBoolean(); + case DataEnumBase::typeKey: + return new DataEnum(); + case ViewModelPropertyEnumBase::typeKey: + return new ViewModelPropertyEnum(); + case ViewModelPropertyColorBase::typeKey: + return new ViewModelPropertyColor(); + case ViewModelInstanceBooleanBase::typeKey: + return new ViewModelInstanceBoolean(); + case ViewModelInstanceListBase::typeKey: + return new ViewModelInstanceList(); + case ViewModelInstanceNumberBase::typeKey: + return new ViewModelInstanceNumber(); + case ViewModelPropertyStringBase::typeKey: + return new ViewModelPropertyString(); + case ViewModelInstanceViewModelBase::typeKey: + return new ViewModelInstanceViewModel(); + case DataEnumValueBase::typeKey: + return new DataEnumValue(); case DrawTargetBase::typeKey: return new DrawTarget(); case CustomPropertyNumberBase::typeKey: @@ -182,8 +285,22 @@ class CoreRegistry return new NestedArtboard(); case SoloBase::typeKey: return new Solo(); + case NestedArtboardLayoutBase::typeKey: + return new NestedArtboardLayout(); + case NSlicerTileModeBase::typeKey: + return new NSlicerTileMode(); + case AxisYBase::typeKey: + return new AxisY(); + case LayoutComponentStyleBase::typeKey: + return new LayoutComponentStyle(); + case AxisXBase::typeKey: + return new AxisX(); + case NSlicerBase::typeKey: + return new NSlicer(); case ListenerFireEventBase::typeKey: return new ListenerFireEvent(); + case KeyFrameUintBase::typeKey: + return new KeyFrameUint(); case NestedSimpleAnimationBase::typeKey: return new NestedSimpleAnimation(); case AnimationStateBase::typeKey: @@ -206,6 +323,10 @@ class CoreRegistry return new KeyedProperty(); case StateMachineListenerBase::typeKey: return new StateMachineListener(); + case TransitionPropertyArtboardComparatorBase::typeKey: + return new TransitionPropertyArtboardComparator(); + case TransitionPropertyViewModelComparatorBase::typeKey: + return new TransitionPropertyViewModelComparator(); case KeyFrameIdBase::typeKey: return new KeyFrameId(); case KeyFrameBoolBase::typeKey: @@ -216,6 +337,12 @@ class CoreRegistry return new ListenerAlignTarget(); case TransitionNumberConditionBase::typeKey: return new TransitionNumberCondition(); + case TransitionValueBooleanComparatorBase::typeKey: + return new TransitionValueBooleanComparator(); + case TransitionViewModelConditionBase::typeKey: + return new TransitionViewModelCondition(); + case TransitionArtboardConditionBase::typeKey: + return new TransitionArtboardCondition(); case AnyStateBase::typeKey: return new AnyState(); case CubicInterpolatorComponentBase::typeKey: @@ -246,10 +373,16 @@ class CoreRegistry return new LinearAnimation(); case StateMachineTriggerBase::typeKey: return new StateMachineTrigger(); + case TransitionValueColorComparatorBase::typeKey: + return new TransitionValueColorComparator(); case ListenerTriggerChangeBase::typeKey: return new ListenerTriggerChange(); case BlendStateDirectBase::typeKey: return new BlendStateDirect(); + case ListenerViewModelChangeBase::typeKey: + return new ListenerViewModelChange(); + case TransitionValueNumberComparatorBase::typeKey: + return new TransitionValueNumberComparator(); case NestedStateMachineBase::typeKey: return new NestedStateMachine(); case ElasticInterpolatorBase::typeKey: @@ -260,8 +393,12 @@ class CoreRegistry return new NestedNumber(); case BlendState1DBase::typeKey: return new BlendState1D(); + case TransitionValueEnumComparatorBase::typeKey: + return new TransitionValueEnumComparator(); case KeyFrameCallbackBase::typeKey: return new KeyFrameCallback(); + case TransitionValueStringComparatorBase::typeKey: + return new TransitionValueStringComparator(); case NestedRemapAnimationBase::typeKey: return new NestedRemapAnimation(); case TransitionBoolConditionBase::typeKey: @@ -324,6 +461,8 @@ class CoreRegistry return new DrawRules(); case CustomPropertyBooleanBase::typeKey: return new CustomPropertyBoolean(); + case LayoutComponentBase::typeKey: + return new LayoutComponent(); case ArtboardBase::typeKey: return new Artboard(); case JoystickBase::typeKey: @@ -332,6 +471,32 @@ class CoreRegistry return new Backboard(); case OpenUrlEventBase::typeKey: return new OpenUrlEvent(); + case BindablePropertyBooleanBase::typeKey: + return new BindablePropertyBoolean(); + case DataBindBase::typeKey: + return new DataBind(); + case DataConverterGroupItemBase::typeKey: + return new DataConverterGroupItem(); + case DataConverterGroupBase::typeKey: + return new DataConverterGroup(); + case DataConverterRounderBase::typeKey: + return new DataConverterRounder(); + case DataConverterOperationBase::typeKey: + return new DataConverterOperation(); + case DataConverterToStringBase::typeKey: + return new DataConverterToString(); + case DataBindContextBase::typeKey: + return new DataBindContext(); + case BindablePropertyStringBase::typeKey: + return new BindablePropertyString(); + case BindablePropertyNumberBase::typeKey: + return new BindablePropertyNumber(); + case BindablePropertyEnumBase::typeKey: + return new BindablePropertyEnum(); + case BindablePropertyColorBase::typeKey: + return new BindablePropertyColor(); + case NestedArtboardLeafBase::typeKey: + return new NestedArtboardLeaf(); case WeightBase::typeKey: return new Weight(); case BoneBase::typeKey: @@ -377,36 +542,105 @@ class CoreRegistry } return nullptr; } - static void setString(Core* object, int propertyKey, std::string value) + static void setBool(Core* object, int propertyKey, bool value) { switch (propertyKey) { - case ComponentBase::namePropertyKey: - object->as()->name(value); + case ViewModelInstanceListItemBase::useLinkedArtboardPropertyKey: + object->as()->useLinkedArtboard(value); break; - case AnimationBase::namePropertyKey: - object->as()->name(value); + case ViewModelInstanceBooleanBase::propertyValuePropertyKey: + object->as()->propertyValue(value); break; - case StateMachineComponentBase::namePropertyKey: - object->as()->name(value); + case TransformComponentConstraintBase::offsetPropertyKey: + object->as()->offset(value); break; - case KeyFrameStringBase::valuePropertyKey: - object->as()->value(value); + case TransformComponentConstraintBase::doesCopyPropertyKey: + object->as()->doesCopy(value); break; - case OpenUrlEventBase::urlPropertyKey: - object->as()->url(value); + case TransformComponentConstraintBase::minPropertyKey: + object->as()->min(value); break; - case TextValueRunBase::textPropertyKey: - object->as()->text(value); + case TransformComponentConstraintBase::maxPropertyKey: + object->as()->max(value); break; - case CustomPropertyStringBase::propertyValuePropertyKey: - object->as()->propertyValue(value); + case TransformComponentConstraintYBase::doesCopyYPropertyKey: + object->as()->doesCopyY(value); break; - case AssetBase::namePropertyKey: - object->as()->name(value); + case TransformComponentConstraintYBase::minYPropertyKey: + object->as()->minY(value); break; - case FileAssetBase::cdnBaseUrlPropertyKey: - object->as()->cdnBaseUrl(value); + case TransformComponentConstraintYBase::maxYPropertyKey: + object->as()->maxY(value); + break; + case IKConstraintBase::invertDirectionPropertyKey: + object->as()->invertDirection(value); + break; + case FollowPathConstraintBase::orientPropertyKey: + object->as()->orient(value); + break; + case FollowPathConstraintBase::offsetPropertyKey: + object->as()->offset(value); + break; + case AxisBase::normalizedPropertyKey: + object->as()->normalized(value); + break; + case LayoutComponentStyleBase::intrinsicallySizedValuePropertyKey: + object->as()->intrinsicallySizedValue(value); + break; + case LayoutComponentStyleBase::linkCornerRadiusPropertyKey: + object->as()->linkCornerRadius(value); + break; + case NestedSimpleAnimationBase::isPlayingPropertyKey: + object->as()->isPlaying(value); + break; + case KeyFrameBoolBase::valuePropertyKey: + object->as()->value(value); + break; + case ListenerAlignTargetBase::preserveOffsetPropertyKey: + object->as()->preserveOffset(value); + break; + case TransitionValueBooleanComparatorBase::valuePropertyKey: + object->as()->value(value); + break; + case NestedBoolBase::nestedValuePropertyKey: + object->as()->nestedValue(value); + break; + case LinearAnimationBase::enableWorkAreaPropertyKey: + object->as()->enableWorkArea(value); + break; + case LinearAnimationBase::quantizePropertyKey: + object->as()->quantize(value); + break; + case StateMachineBoolBase::valuePropertyKey: + object->as()->value(value); + break; + case ShapePaintBase::isVisiblePropertyKey: + object->as()->isVisible(value); + break; + case StrokeBase::transformAffectsStrokePropertyKey: + object->as()->transformAffectsStroke(value); + break; + case PointsPathBase::isClosedPropertyKey: + object->as()->isClosed(value); + break; + case RectangleBase::linkCornerRadiusPropertyKey: + object->as()->linkCornerRadius(value); + break; + case ClippingShapeBase::isVisiblePropertyKey: + object->as()->isVisible(value); + break; + case CustomPropertyBooleanBase::propertyValuePropertyKey: + object->as()->propertyValue(value); + break; + case LayoutComponentBase::clipPropertyKey: + object->as()->clip(value); + break; + case BindablePropertyBooleanBase::propertyValuePropertyKey: + object->as()->propertyValue(value); + break; + case TextModifierRangeBase::clampPropertyKey: + object->as()->clamp(value); break; } } @@ -414,9 +648,39 @@ class CoreRegistry { switch (propertyKey) { + case ViewModelInstanceListItemBase::viewModelIdPropertyKey: + object->as()->viewModelId(value); + break; + case ViewModelInstanceListItemBase::viewModelInstanceIdPropertyKey: + object->as()->viewModelInstanceId(value); + break; + case ViewModelInstanceListItemBase::artboardIdPropertyKey: + object->as()->artboardId(value); + break; + case ViewModelInstanceValueBase::viewModelPropertyIdPropertyKey: + object->as()->viewModelPropertyId(value); + break; + case ViewModelInstanceEnumBase::propertyValuePropertyKey: + object->as()->propertyValue(value); + break; + case ViewModelBase::defaultInstanceIdPropertyKey: + object->as()->defaultInstanceId(value); + break; + case ViewModelPropertyViewModelBase::viewModelReferenceIdPropertyKey: + object->as()->viewModelReferenceId(value); + break; case ComponentBase::parentIdPropertyKey: object->as()->parentId(value); break; + case ViewModelInstanceBase::viewModelIdPropertyKey: + object->as()->viewModelId(value); + break; + case ViewModelPropertyEnumBase::enumIdPropertyKey: + object->as()->enumId(value); + break; + case ViewModelInstanceViewModelBase::propertyValuePropertyKey: + object->as()->propertyValue(value); + break; case DrawTargetBase::drawableIdPropertyKey: object->as()->drawableId(value); break; @@ -456,9 +720,162 @@ class CoreRegistry case SoloBase::activeComponentIdPropertyKey: object->as()->activeComponentId(value); break; + case NestedArtboardLayoutBase::instanceWidthUnitsValuePropertyKey: + object->as()->instanceWidthUnitsValue(value); + break; + case NestedArtboardLayoutBase::instanceHeightUnitsValuePropertyKey: + object->as()->instanceHeightUnitsValue(value); + break; + case NestedArtboardLayoutBase::instanceWidthScaleTypePropertyKey: + object->as()->instanceWidthScaleType(value); + break; + case NestedArtboardLayoutBase::instanceHeightScaleTypePropertyKey: + object->as()->instanceHeightScaleType(value); + break; + case NSlicerTileModeBase::patchIndexPropertyKey: + object->as()->patchIndex(value); + break; + case NSlicerTileModeBase::stylePropertyKey: + object->as()->style(value); + break; + case LayoutComponentStyleBase::layoutWidthScaleTypePropertyKey: + object->as()->layoutWidthScaleType(value); + break; + case LayoutComponentStyleBase::layoutHeightScaleTypePropertyKey: + object->as()->layoutHeightScaleType(value); + break; + case LayoutComponentStyleBase::layoutAlignmentTypePropertyKey: + object->as()->layoutAlignmentType(value); + break; + case LayoutComponentStyleBase::animationStyleTypePropertyKey: + object->as()->animationStyleType(value); + break; + case LayoutComponentStyleBase::interpolationTypePropertyKey: + object->as()->interpolationType(value); + break; + case LayoutComponentStyleBase::interpolatorIdPropertyKey: + object->as()->interpolatorId(value); + break; + case LayoutComponentStyleBase::displayValuePropertyKey: + object->as()->displayValue(value); + break; + case LayoutComponentStyleBase::positionTypeValuePropertyKey: + object->as()->positionTypeValue(value); + break; + case LayoutComponentStyleBase::flexDirectionValuePropertyKey: + object->as()->flexDirectionValue(value); + break; + case LayoutComponentStyleBase::directionValuePropertyKey: + object->as()->directionValue(value); + break; + case LayoutComponentStyleBase::alignContentValuePropertyKey: + object->as()->alignContentValue(value); + break; + case LayoutComponentStyleBase::alignItemsValuePropertyKey: + object->as()->alignItemsValue(value); + break; + case LayoutComponentStyleBase::alignSelfValuePropertyKey: + object->as()->alignSelfValue(value); + break; + case LayoutComponentStyleBase::justifyContentValuePropertyKey: + object->as()->justifyContentValue(value); + break; + case LayoutComponentStyleBase::flexWrapValuePropertyKey: + object->as()->flexWrapValue(value); + break; + case LayoutComponentStyleBase::overflowValuePropertyKey: + object->as()->overflowValue(value); + break; + case LayoutComponentStyleBase::widthUnitsValuePropertyKey: + object->as()->widthUnitsValue(value); + break; + case LayoutComponentStyleBase::heightUnitsValuePropertyKey: + object->as()->heightUnitsValue(value); + break; + case LayoutComponentStyleBase::borderLeftUnitsValuePropertyKey: + object->as()->borderLeftUnitsValue(value); + break; + case LayoutComponentStyleBase::borderRightUnitsValuePropertyKey: + object->as()->borderRightUnitsValue(value); + break; + case LayoutComponentStyleBase::borderTopUnitsValuePropertyKey: + object->as()->borderTopUnitsValue(value); + break; + case LayoutComponentStyleBase::borderBottomUnitsValuePropertyKey: + object->as()->borderBottomUnitsValue(value); + break; + case LayoutComponentStyleBase::marginLeftUnitsValuePropertyKey: + object->as()->marginLeftUnitsValue(value); + break; + case LayoutComponentStyleBase::marginRightUnitsValuePropertyKey: + object->as()->marginRightUnitsValue(value); + break; + case LayoutComponentStyleBase::marginTopUnitsValuePropertyKey: + object->as()->marginTopUnitsValue(value); + break; + case LayoutComponentStyleBase::marginBottomUnitsValuePropertyKey: + object->as()->marginBottomUnitsValue(value); + break; + case LayoutComponentStyleBase::paddingLeftUnitsValuePropertyKey: + object->as()->paddingLeftUnitsValue(value); + break; + case LayoutComponentStyleBase::paddingRightUnitsValuePropertyKey: + object->as()->paddingRightUnitsValue(value); + break; + case LayoutComponentStyleBase::paddingTopUnitsValuePropertyKey: + object->as()->paddingTopUnitsValue(value); + break; + case LayoutComponentStyleBase::paddingBottomUnitsValuePropertyKey: + object->as()->paddingBottomUnitsValue(value); + break; + case LayoutComponentStyleBase::positionLeftUnitsValuePropertyKey: + object->as()->positionLeftUnitsValue(value); + break; + case LayoutComponentStyleBase::positionRightUnitsValuePropertyKey: + object->as()->positionRightUnitsValue(value); + break; + case LayoutComponentStyleBase::positionTopUnitsValuePropertyKey: + object->as()->positionTopUnitsValue(value); + break; + case LayoutComponentStyleBase::positionBottomUnitsValuePropertyKey: + object->as()->positionBottomUnitsValue(value); + break; + case LayoutComponentStyleBase::gapHorizontalUnitsValuePropertyKey: + object->as()->gapHorizontalUnitsValue(value); + break; + case LayoutComponentStyleBase::gapVerticalUnitsValuePropertyKey: + object->as()->gapVerticalUnitsValue(value); + break; + case LayoutComponentStyleBase::minWidthUnitsValuePropertyKey: + object->as()->minWidthUnitsValue(value); + break; + case LayoutComponentStyleBase::minHeightUnitsValuePropertyKey: + object->as()->minHeightUnitsValue(value); + break; + case LayoutComponentStyleBase::maxWidthUnitsValuePropertyKey: + object->as()->maxWidthUnitsValue(value); + break; + case LayoutComponentStyleBase::maxHeightUnitsValuePropertyKey: + object->as()->maxHeightUnitsValue(value); + break; case ListenerFireEventBase::eventIdPropertyKey: object->as()->eventId(value); break; + case LayerStateBase::flagsPropertyKey: + object->as()->flags(value); + break; + case KeyFrameBase::framePropertyKey: + object->as()->frame(value); + break; + case InterpolatingKeyFrameBase::interpolationTypePropertyKey: + object->as()->interpolationType(value); + break; + case InterpolatingKeyFrameBase::interpolatorIdPropertyKey: + object->as()->interpolatorId(value); + break; + case KeyFrameUintBase::valuePropertyKey: + object->as()->value(value); + break; case ListenerInputChangeBase::inputIdPropertyKey: object->as()->inputId(value); break; @@ -483,8 +900,8 @@ class CoreRegistry case BlendAnimationDirectBase::blendSourcePropertyKey: object->as()->blendSource(value); break; - case TransitionConditionBase::inputIdPropertyKey: - object->as()->inputId(value); + case TransitionInputConditionBase::inputIdPropertyKey: + object->as()->inputId(value); break; case KeyedPropertyBase::propertyKeyPropertyKey: object->as()->propertyKey(value); @@ -498,14 +915,8 @@ class CoreRegistry case StateMachineListenerBase::eventIdPropertyKey: object->as()->eventId(value); break; - case KeyFrameBase::framePropertyKey: - object->as()->frame(value); - break; - case InterpolatingKeyFrameBase::interpolationTypePropertyKey: - object->as()->interpolationType(value); - break; - case InterpolatingKeyFrameBase::interpolatorIdPropertyKey: - object->as()->interpolatorId(value); + case TransitionPropertyArtboardComparatorBase::propertyTypePropertyKey: + object->as()->propertyType(value); break; case KeyFrameIdBase::valuePropertyKey: object->as()->value(value); @@ -519,6 +930,15 @@ class CoreRegistry case TransitionValueConditionBase::opValuePropertyKey: object->as()->opValue(value); break; + case TransitionViewModelConditionBase::leftComparatorIdPropertyKey: + object->as()->leftComparatorId(value); + break; + case TransitionViewModelConditionBase::rightComparatorIdPropertyKey: + object->as()->rightComparatorId(value); + break; + case TransitionViewModelConditionBase::opValuePropertyKey: + object->as()->opValue(value); + break; case StateTransitionBase::stateToIdPropertyKey: object->as()->stateToId(value); break; @@ -537,6 +957,9 @@ class CoreRegistry case StateTransitionBase::interpolatorIdPropertyKey: object->as()->interpolatorId(value); break; + case StateTransitionBase::randomWeightPropertyKey: + object->as()->randomWeight(value); + break; case StateMachineFireEventBase::eventIdPropertyKey: object->as()->eventId(value); break; @@ -564,6 +987,9 @@ class CoreRegistry case BlendState1DBase::inputIdPropertyKey: object->as()->inputId(value); break; + case TransitionValueEnumComparatorBase::valuePropertyKey: + object->as()->value(value); + break; case BlendStateTransitionBase::exitBlendAnimationIdPropertyKey: object->as()->exitBlendAnimationId(value); break; @@ -597,9 +1023,15 @@ class CoreRegistry case DrawRulesBase::drawTargetIdPropertyKey: object->as()->drawTargetId(value); break; + case LayoutComponentBase::styleIdPropertyKey: + object->as()->styleId(value); + break; case ArtboardBase::defaultStateMachineIdPropertyKey: object->as()->defaultStateMachineId(value); break; + case ArtboardBase::viewModelIdPropertyKey: + object->as()->viewModelId(value); + break; case JoystickBase::xIdPropertyKey: object->as()->xId(value); break; @@ -615,6 +1047,30 @@ class CoreRegistry case OpenUrlEventBase::targetValuePropertyKey: object->as()->targetValue(value); break; + case DataBindBase::propertyKeyPropertyKey: + object->as()->propertyKey(value); + break; + case DataBindBase::flagsPropertyKey: + object->as()->flags(value); + break; + case DataBindBase::converterIdPropertyKey: + object->as()->converterId(value); + break; + case DataConverterGroupItemBase::converterIdPropertyKey: + object->as()->converterId(value); + break; + case DataConverterRounderBase::decimalsPropertyKey: + object->as()->decimals(value); + break; + case DataConverterOperationBase::operationTypePropertyKey: + object->as()->operationType(value); + break; + case BindablePropertyEnumBase::propertyValuePropertyKey: + object->as()->propertyValue(value); + break; + case NestedArtboardLeafBase::fitPropertyKey: + object->as()->fit(value); + break; case WeightBase::valuesPropertyKey: object->as()->values(value); break; @@ -689,10 +1145,91 @@ class CoreRegistry break; } } + static void setColor(Core* object, int propertyKey, int value) + { + switch (propertyKey) + { + case ViewModelInstanceColorBase::propertyValuePropertyKey: + object->as()->propertyValue(value); + break; + case KeyFrameColorBase::valuePropertyKey: + object->as()->value(value); + break; + case TransitionValueColorComparatorBase::valuePropertyKey: + object->as()->value(value); + break; + case SolidColorBase::colorValuePropertyKey: + object->as()->colorValue(value); + break; + case GradientStopBase::colorValuePropertyKey: + object->as()->colorValue(value); + break; + case BindablePropertyColorBase::propertyValuePropertyKey: + object->as()->propertyValue(value); + break; + } + } + static void setString(Core* object, int propertyKey, std::string value) + { + switch (propertyKey) + { + case ViewModelComponentBase::namePropertyKey: + object->as()->name(value); + break; + case ViewModelInstanceStringBase::propertyValuePropertyKey: + object->as()->propertyValue(value); + break; + case ComponentBase::namePropertyKey: + object->as()->name(value); + break; + case DataEnumValueBase::keyPropertyKey: + object->as()->key(value); + break; + case DataEnumValueBase::valuePropertyKey: + object->as()->value(value); + break; + case AnimationBase::namePropertyKey: + object->as()->name(value); + break; + case StateMachineComponentBase::namePropertyKey: + object->as()->name(value); + break; + case KeyFrameStringBase::valuePropertyKey: + object->as()->value(value); + break; + case TransitionValueStringComparatorBase::valuePropertyKey: + object->as()->value(value); + break; + case OpenUrlEventBase::urlPropertyKey: + object->as()->url(value); + break; + case DataConverterBase::namePropertyKey: + object->as()->name(value); + break; + case BindablePropertyStringBase::propertyValuePropertyKey: + object->as()->propertyValue(value); + break; + case TextValueRunBase::textPropertyKey: + object->as()->text(value); + break; + case CustomPropertyStringBase::propertyValuePropertyKey: + object->as()->propertyValue(value); + break; + case AssetBase::namePropertyKey: + object->as()->name(value); + break; + case FileAssetBase::cdnBaseUrlPropertyKey: + object->as()->cdnBaseUrl(value); + break; + } + } static void setDouble(Core* object, int propertyKey, float value) { switch (propertyKey) { + case ViewModelInstanceNumberBase::propertyValuePropertyKey: + object->as()->propertyValue(value); + break; case CustomPropertyNumberBase::propertyValuePropertyKey: object->as()->propertyValue(value); break; @@ -742,37 +1279,144 @@ class CoreRegistry object->as()->scaleY(value); break; case NodeBase::xPropertyKey: + case NodeBase::xArtboardPropertyKey: object->as()->x(value); break; case NodeBase::yPropertyKey: + case NodeBase::yArtboardPropertyKey: object->as()->y(value); break; - case NestedLinearAnimationBase::mixPropertyKey: - object->as()->mix(value); + case NestedArtboardLayoutBase::instanceWidthPropertyKey: + object->as()->instanceWidth(value); break; - case NestedSimpleAnimationBase::speedPropertyKey: - object->as()->speed(value); + case NestedArtboardLayoutBase::instanceHeightPropertyKey: + object->as()->instanceHeight(value); break; - case AdvanceableStateBase::speedPropertyKey: - object->as()->speed(value); + case AxisBase::offsetPropertyKey: + object->as()->offset(value); break; - case BlendAnimationDirectBase::mixValuePropertyKey: - object->as()->mixValue(value); + case LayoutComponentStyleBase::gapHorizontalPropertyKey: + object->as()->gapHorizontal(value); break; - case StateMachineNumberBase::valuePropertyKey: - object->as()->value(value); + case LayoutComponentStyleBase::gapVerticalPropertyKey: + object->as()->gapVertical(value); break; - case CubicInterpolatorBase::x1PropertyKey: - object->as()->x1(value); + case LayoutComponentStyleBase::maxWidthPropertyKey: + object->as()->maxWidth(value); break; - case CubicInterpolatorBase::y1PropertyKey: - object->as()->y1(value); + case LayoutComponentStyleBase::maxHeightPropertyKey: + object->as()->maxHeight(value); break; - case CubicInterpolatorBase::x2PropertyKey: - object->as()->x2(value); + case LayoutComponentStyleBase::minWidthPropertyKey: + object->as()->minWidth(value); break; - case CubicInterpolatorBase::y2PropertyKey: - object->as()->y2(value); + case LayoutComponentStyleBase::minHeightPropertyKey: + object->as()->minHeight(value); + break; + case LayoutComponentStyleBase::borderLeftPropertyKey: + object->as()->borderLeft(value); + break; + case LayoutComponentStyleBase::borderRightPropertyKey: + object->as()->borderRight(value); + break; + case LayoutComponentStyleBase::borderTopPropertyKey: + object->as()->borderTop(value); + break; + case LayoutComponentStyleBase::borderBottomPropertyKey: + object->as()->borderBottom(value); + break; + case LayoutComponentStyleBase::marginLeftPropertyKey: + object->as()->marginLeft(value); + break; + case LayoutComponentStyleBase::marginRightPropertyKey: + object->as()->marginRight(value); + break; + case LayoutComponentStyleBase::marginTopPropertyKey: + object->as()->marginTop(value); + break; + case LayoutComponentStyleBase::marginBottomPropertyKey: + object->as()->marginBottom(value); + break; + case LayoutComponentStyleBase::paddingLeftPropertyKey: + object->as()->paddingLeft(value); + break; + case LayoutComponentStyleBase::paddingRightPropertyKey: + object->as()->paddingRight(value); + break; + case LayoutComponentStyleBase::paddingTopPropertyKey: + object->as()->paddingTop(value); + break; + case LayoutComponentStyleBase::paddingBottomPropertyKey: + object->as()->paddingBottom(value); + break; + case LayoutComponentStyleBase::positionLeftPropertyKey: + object->as()->positionLeft(value); + break; + case LayoutComponentStyleBase::positionRightPropertyKey: + object->as()->positionRight(value); + break; + case LayoutComponentStyleBase::positionTopPropertyKey: + object->as()->positionTop(value); + break; + case LayoutComponentStyleBase::positionBottomPropertyKey: + object->as()->positionBottom(value); + break; + case LayoutComponentStyleBase::flexPropertyKey: + object->as()->flex(value); + break; + case LayoutComponentStyleBase::flexGrowPropertyKey: + object->as()->flexGrow(value); + break; + case LayoutComponentStyleBase::flexShrinkPropertyKey: + object->as()->flexShrink(value); + break; + case LayoutComponentStyleBase::flexBasisPropertyKey: + object->as()->flexBasis(value); + break; + case LayoutComponentStyleBase::aspectRatioPropertyKey: + object->as()->aspectRatio(value); + break; + case LayoutComponentStyleBase::interpolationTimePropertyKey: + object->as()->interpolationTime(value); + break; + case LayoutComponentStyleBase::cornerRadiusTLPropertyKey: + object->as()->cornerRadiusTL(value); + break; + case LayoutComponentStyleBase::cornerRadiusTRPropertyKey: + object->as()->cornerRadiusTR(value); + break; + case LayoutComponentStyleBase::cornerRadiusBLPropertyKey: + object->as()->cornerRadiusBL(value); + break; + case LayoutComponentStyleBase::cornerRadiusBRPropertyKey: + object->as()->cornerRadiusBR(value); + break; + case NestedLinearAnimationBase::mixPropertyKey: + object->as()->mix(value); + break; + case NestedSimpleAnimationBase::speedPropertyKey: + object->as()->speed(value); + break; + case AdvanceableStateBase::speedPropertyKey: + object->as()->speed(value); + break; + case BlendAnimationDirectBase::mixValuePropertyKey: + object->as()->mixValue(value); + break; + case StateMachineNumberBase::valuePropertyKey: + object->as()->value(value); + break; + case CubicInterpolatorBase::x1PropertyKey: + object->as()->x1(value); + break; + case CubicInterpolatorBase::y1PropertyKey: + object->as()->y1(value); + break; + case CubicInterpolatorBase::x2PropertyKey: + object->as()->x2(value); + break; + case CubicInterpolatorBase::y2PropertyKey: + object->as()->y2(value); break; case TransitionNumberConditionBase::valuePropertyKey: object->as()->value(value); @@ -798,6 +1442,9 @@ class CoreRegistry case LinearAnimationBase::speedPropertyKey: object->as()->speed(value); break; + case TransitionValueNumberComparatorBase::valuePropertyKey: + object->as()->value(value); + break; case ElasticInterpolatorBase::amplitudePropertyKey: object->as()->amplitude(value); break; @@ -921,17 +1568,11 @@ class CoreRegistry case CubicDetachedVertexBase::outDistancePropertyKey: object->as()->outDistance(value); break; - case ArtboardBase::widthPropertyKey: - object->as()->width(value); - break; - case ArtboardBase::heightPropertyKey: - object->as()->height(value); - break; - case ArtboardBase::xPropertyKey: - object->as()->x(value); + case LayoutComponentBase::widthPropertyKey: + object->as()->width(value); break; - case ArtboardBase::yPropertyKey: - object->as()->y(value); + case LayoutComponentBase::heightPropertyKey: + object->as()->height(value); break; case ArtboardBase::originXPropertyKey: object->as()->originX(value); @@ -963,6 +1604,18 @@ class CoreRegistry case JoystickBase::heightPropertyKey: object->as()->height(value); break; + case DataConverterOperationBase::valuePropertyKey: + object->as()->value(value); + break; + case BindablePropertyNumberBase::propertyValuePropertyKey: + object->as()->propertyValue(value); + break; + case NestedArtboardLeafBase::alignmentXPropertyKey: + object->as()->alignmentX(value); + break; + case NestedArtboardLeafBase::alignmentYPropertyKey: + object->as()->alignmentY(value); + break; case BoneBase::lengthPropertyKey: object->as()->length(value); break; @@ -1086,144 +1739,120 @@ class CoreRegistry case DrawableAssetBase::widthPropertyKey: object->as()->width(value); break; + case ExportAudioBase::volumePropertyKey: + object->as()->volume(value); + break; } } - static void setBool(Core* object, int propertyKey, bool value) + static void setCallback(Core* object, int propertyKey, CallbackData value) { switch (propertyKey) { - case TransformComponentConstraintBase::offsetPropertyKey: - object->as()->offset(value); + case NestedTriggerBase::firePropertyKey: + object->as()->fire(value); break; - case TransformComponentConstraintBase::doesCopyPropertyKey: - object->as()->doesCopy(value); + case EventBase::triggerPropertyKey: + object->as()->trigger(value); break; + } + } + static bool getBool(Core* object, int propertyKey) + { + switch (propertyKey) + { + case ViewModelInstanceListItemBase::useLinkedArtboardPropertyKey: + return object->as()->useLinkedArtboard(); + case ViewModelInstanceBooleanBase::propertyValuePropertyKey: + return object->as()->propertyValue(); + case TransformComponentConstraintBase::offsetPropertyKey: + return object->as()->offset(); + case TransformComponentConstraintBase::doesCopyPropertyKey: + return object->as()->doesCopy(); case TransformComponentConstraintBase::minPropertyKey: - object->as()->min(value); - break; + return object->as()->min(); case TransformComponentConstraintBase::maxPropertyKey: - object->as()->max(value); - break; + return object->as()->max(); case TransformComponentConstraintYBase::doesCopyYPropertyKey: - object->as()->doesCopyY(value); - break; + return object->as()->doesCopyY(); case TransformComponentConstraintYBase::minYPropertyKey: - object->as()->minY(value); - break; + return object->as()->minY(); case TransformComponentConstraintYBase::maxYPropertyKey: - object->as()->maxY(value); - break; + return object->as()->maxY(); case IKConstraintBase::invertDirectionPropertyKey: - object->as()->invertDirection(value); - break; + return object->as()->invertDirection(); case FollowPathConstraintBase::orientPropertyKey: - object->as()->orient(value); - break; + return object->as()->orient(); case FollowPathConstraintBase::offsetPropertyKey: - object->as()->offset(value); - break; + return object->as()->offset(); + case AxisBase::normalizedPropertyKey: + return object->as()->normalized(); + case LayoutComponentStyleBase::intrinsicallySizedValuePropertyKey: + return object->as()->intrinsicallySizedValue(); + case LayoutComponentStyleBase::linkCornerRadiusPropertyKey: + return object->as()->linkCornerRadius(); case NestedSimpleAnimationBase::isPlayingPropertyKey: - object->as()->isPlaying(value); - break; + return object->as()->isPlaying(); case KeyFrameBoolBase::valuePropertyKey: - object->as()->value(value); - break; + return object->as()->value(); + case ListenerAlignTargetBase::preserveOffsetPropertyKey: + return object->as()->preserveOffset(); + case TransitionValueBooleanComparatorBase::valuePropertyKey: + return object->as()->value(); case NestedBoolBase::nestedValuePropertyKey: - object->as()->nestedValue(value); - break; + return object->as()->nestedValue(); case LinearAnimationBase::enableWorkAreaPropertyKey: - object->as()->enableWorkArea(value); - break; + return object->as()->enableWorkArea(); case LinearAnimationBase::quantizePropertyKey: - object->as()->quantize(value); - break; + return object->as()->quantize(); case StateMachineBoolBase::valuePropertyKey: - object->as()->value(value); - break; + return object->as()->value(); case ShapePaintBase::isVisiblePropertyKey: - object->as()->isVisible(value); - break; + return object->as()->isVisible(); case StrokeBase::transformAffectsStrokePropertyKey: - object->as()->transformAffectsStroke(value); - break; + return object->as()->transformAffectsStroke(); case PointsPathBase::isClosedPropertyKey: - object->as()->isClosed(value); - break; + return object->as()->isClosed(); case RectangleBase::linkCornerRadiusPropertyKey: - object->as()->linkCornerRadius(value); - break; + return object->as()->linkCornerRadius(); case ClippingShapeBase::isVisiblePropertyKey: - object->as()->isVisible(value); - break; + return object->as()->isVisible(); case CustomPropertyBooleanBase::propertyValuePropertyKey: - object->as()->propertyValue(value); - break; - case ArtboardBase::clipPropertyKey: - object->as()->clip(value); - break; + return object->as()->propertyValue(); + case LayoutComponentBase::clipPropertyKey: + return object->as()->clip(); + case BindablePropertyBooleanBase::propertyValuePropertyKey: + return object->as()->propertyValue(); case TextModifierRangeBase::clampPropertyKey: - object->as()->clamp(value); - break; - } - } - static void setCallback(Core* object, int propertyKey, CallbackData value) - { - switch (propertyKey) - { - case NestedTriggerBase::firePropertyKey: - object->as()->fire(value); - break; - case EventBase::triggerPropertyKey: - object->as()->trigger(value); - break; - } - } - static void setColor(Core* object, int propertyKey, int value) - { - switch (propertyKey) - { - case KeyFrameColorBase::valuePropertyKey: - object->as()->value(value); - break; - case SolidColorBase::colorValuePropertyKey: - object->as()->colorValue(value); - break; - case GradientStopBase::colorValuePropertyKey: - object->as()->colorValue(value); - break; - } - } - static std::string getString(Core* object, int propertyKey) - { - switch (propertyKey) - { - case ComponentBase::namePropertyKey: - return object->as()->name(); - case AnimationBase::namePropertyKey: - return object->as()->name(); - case StateMachineComponentBase::namePropertyKey: - return object->as()->name(); - case KeyFrameStringBase::valuePropertyKey: - return object->as()->value(); - case OpenUrlEventBase::urlPropertyKey: - return object->as()->url(); - case TextValueRunBase::textPropertyKey: - return object->as()->text(); - case CustomPropertyStringBase::propertyValuePropertyKey: - return object->as()->propertyValue(); - case AssetBase::namePropertyKey: - return object->as()->name(); - case FileAssetBase::cdnBaseUrlPropertyKey: - return object->as()->cdnBaseUrl(); + return object->as()->clamp(); } - return ""; + return false; } static uint32_t getUint(Core* object, int propertyKey) { switch (propertyKey) { + case ViewModelInstanceListItemBase::viewModelIdPropertyKey: + return object->as()->viewModelId(); + case ViewModelInstanceListItemBase::viewModelInstanceIdPropertyKey: + return object->as()->viewModelInstanceId(); + case ViewModelInstanceListItemBase::artboardIdPropertyKey: + return object->as()->artboardId(); + case ViewModelInstanceValueBase::viewModelPropertyIdPropertyKey: + return object->as()->viewModelPropertyId(); + case ViewModelInstanceEnumBase::propertyValuePropertyKey: + return object->as()->propertyValue(); + case ViewModelBase::defaultInstanceIdPropertyKey: + return object->as()->defaultInstanceId(); + case ViewModelPropertyViewModelBase::viewModelReferenceIdPropertyKey: + return object->as()->viewModelReferenceId(); case ComponentBase::parentIdPropertyKey: return object->as()->parentId(); + case ViewModelInstanceBase::viewModelIdPropertyKey: + return object->as()->viewModelId(); + case ViewModelPropertyEnumBase::enumIdPropertyKey: + return object->as()->enumId(); + case ViewModelInstanceViewModelBase::propertyValuePropertyKey: + return object->as()->propertyValue(); case DrawTargetBase::drawableIdPropertyKey: return object->as()->drawableId(); case DrawTargetBase::placementValuePropertyKey: @@ -1250,8 +1879,110 @@ class CoreRegistry return object->as()->animationId(); case SoloBase::activeComponentIdPropertyKey: return object->as()->activeComponentId(); + case NestedArtboardLayoutBase::instanceWidthUnitsValuePropertyKey: + return object->as()->instanceWidthUnitsValue(); + case NestedArtboardLayoutBase::instanceHeightUnitsValuePropertyKey: + return object->as()->instanceHeightUnitsValue(); + case NestedArtboardLayoutBase::instanceWidthScaleTypePropertyKey: + return object->as()->instanceWidthScaleType(); + case NestedArtboardLayoutBase::instanceHeightScaleTypePropertyKey: + return object->as()->instanceHeightScaleType(); + case NSlicerTileModeBase::patchIndexPropertyKey: + return object->as()->patchIndex(); + case NSlicerTileModeBase::stylePropertyKey: + return object->as()->style(); + case LayoutComponentStyleBase::layoutWidthScaleTypePropertyKey: + return object->as()->layoutWidthScaleType(); + case LayoutComponentStyleBase::layoutHeightScaleTypePropertyKey: + return object->as()->layoutHeightScaleType(); + case LayoutComponentStyleBase::layoutAlignmentTypePropertyKey: + return object->as()->layoutAlignmentType(); + case LayoutComponentStyleBase::animationStyleTypePropertyKey: + return object->as()->animationStyleType(); + case LayoutComponentStyleBase::interpolationTypePropertyKey: + return object->as()->interpolationType(); + case LayoutComponentStyleBase::interpolatorIdPropertyKey: + return object->as()->interpolatorId(); + case LayoutComponentStyleBase::displayValuePropertyKey: + return object->as()->displayValue(); + case LayoutComponentStyleBase::positionTypeValuePropertyKey: + return object->as()->positionTypeValue(); + case LayoutComponentStyleBase::flexDirectionValuePropertyKey: + return object->as()->flexDirectionValue(); + case LayoutComponentStyleBase::directionValuePropertyKey: + return object->as()->directionValue(); + case LayoutComponentStyleBase::alignContentValuePropertyKey: + return object->as()->alignContentValue(); + case LayoutComponentStyleBase::alignItemsValuePropertyKey: + return object->as()->alignItemsValue(); + case LayoutComponentStyleBase::alignSelfValuePropertyKey: + return object->as()->alignSelfValue(); + case LayoutComponentStyleBase::justifyContentValuePropertyKey: + return object->as()->justifyContentValue(); + case LayoutComponentStyleBase::flexWrapValuePropertyKey: + return object->as()->flexWrapValue(); + case LayoutComponentStyleBase::overflowValuePropertyKey: + return object->as()->overflowValue(); + case LayoutComponentStyleBase::widthUnitsValuePropertyKey: + return object->as()->widthUnitsValue(); + case LayoutComponentStyleBase::heightUnitsValuePropertyKey: + return object->as()->heightUnitsValue(); + case LayoutComponentStyleBase::borderLeftUnitsValuePropertyKey: + return object->as()->borderLeftUnitsValue(); + case LayoutComponentStyleBase::borderRightUnitsValuePropertyKey: + return object->as()->borderRightUnitsValue(); + case LayoutComponentStyleBase::borderTopUnitsValuePropertyKey: + return object->as()->borderTopUnitsValue(); + case LayoutComponentStyleBase::borderBottomUnitsValuePropertyKey: + return object->as()->borderBottomUnitsValue(); + case LayoutComponentStyleBase::marginLeftUnitsValuePropertyKey: + return object->as()->marginLeftUnitsValue(); + case LayoutComponentStyleBase::marginRightUnitsValuePropertyKey: + return object->as()->marginRightUnitsValue(); + case LayoutComponentStyleBase::marginTopUnitsValuePropertyKey: + return object->as()->marginTopUnitsValue(); + case LayoutComponentStyleBase::marginBottomUnitsValuePropertyKey: + return object->as()->marginBottomUnitsValue(); + case LayoutComponentStyleBase::paddingLeftUnitsValuePropertyKey: + return object->as()->paddingLeftUnitsValue(); + case LayoutComponentStyleBase::paddingRightUnitsValuePropertyKey: + return object->as()->paddingRightUnitsValue(); + case LayoutComponentStyleBase::paddingTopUnitsValuePropertyKey: + return object->as()->paddingTopUnitsValue(); + case LayoutComponentStyleBase::paddingBottomUnitsValuePropertyKey: + return object->as()->paddingBottomUnitsValue(); + case LayoutComponentStyleBase::positionLeftUnitsValuePropertyKey: + return object->as()->positionLeftUnitsValue(); + case LayoutComponentStyleBase::positionRightUnitsValuePropertyKey: + return object->as()->positionRightUnitsValue(); + case LayoutComponentStyleBase::positionTopUnitsValuePropertyKey: + return object->as()->positionTopUnitsValue(); + case LayoutComponentStyleBase::positionBottomUnitsValuePropertyKey: + return object->as()->positionBottomUnitsValue(); + case LayoutComponentStyleBase::gapHorizontalUnitsValuePropertyKey: + return object->as()->gapHorizontalUnitsValue(); + case LayoutComponentStyleBase::gapVerticalUnitsValuePropertyKey: + return object->as()->gapVerticalUnitsValue(); + case LayoutComponentStyleBase::minWidthUnitsValuePropertyKey: + return object->as()->minWidthUnitsValue(); + case LayoutComponentStyleBase::minHeightUnitsValuePropertyKey: + return object->as()->minHeightUnitsValue(); + case LayoutComponentStyleBase::maxWidthUnitsValuePropertyKey: + return object->as()->maxWidthUnitsValue(); + case LayoutComponentStyleBase::maxHeightUnitsValuePropertyKey: + return object->as()->maxHeightUnitsValue(); case ListenerFireEventBase::eventIdPropertyKey: return object->as()->eventId(); + case LayerStateBase::flagsPropertyKey: + return object->as()->flags(); + case KeyFrameBase::framePropertyKey: + return object->as()->frame(); + case InterpolatingKeyFrameBase::interpolationTypePropertyKey: + return object->as()->interpolationType(); + case InterpolatingKeyFrameBase::interpolatorIdPropertyKey: + return object->as()->interpolatorId(); + case KeyFrameUintBase::valuePropertyKey: + return object->as()->value(); case ListenerInputChangeBase::inputIdPropertyKey: return object->as()->inputId(); case ListenerInputChangeBase::nestedInputIdPropertyKey: @@ -1268,8 +1999,8 @@ class CoreRegistry return object->as()->inputId(); case BlendAnimationDirectBase::blendSourcePropertyKey: return object->as()->blendSource(); - case TransitionConditionBase::inputIdPropertyKey: - return object->as()->inputId(); + case TransitionInputConditionBase::inputIdPropertyKey: + return object->as()->inputId(); case KeyedPropertyBase::propertyKeyPropertyKey: return object->as()->propertyKey(); case StateMachineListenerBase::targetIdPropertyKey: @@ -1278,12 +2009,8 @@ class CoreRegistry return object->as()->listenerTypeValue(); case StateMachineListenerBase::eventIdPropertyKey: return object->as()->eventId(); - case KeyFrameBase::framePropertyKey: - return object->as()->frame(); - case InterpolatingKeyFrameBase::interpolationTypePropertyKey: - return object->as()->interpolationType(); - case InterpolatingKeyFrameBase::interpolatorIdPropertyKey: - return object->as()->interpolatorId(); + case TransitionPropertyArtboardComparatorBase::propertyTypePropertyKey: + return object->as()->propertyType(); case KeyFrameIdBase::valuePropertyKey: return object->as()->value(); case ListenerBoolChangeBase::valuePropertyKey: @@ -1292,6 +2019,12 @@ class CoreRegistry return object->as()->targetId(); case TransitionValueConditionBase::opValuePropertyKey: return object->as()->opValue(); + case TransitionViewModelConditionBase::leftComparatorIdPropertyKey: + return object->as()->leftComparatorId(); + case TransitionViewModelConditionBase::rightComparatorIdPropertyKey: + return object->as()->rightComparatorId(); + case TransitionViewModelConditionBase::opValuePropertyKey: + return object->as()->opValue(); case StateTransitionBase::stateToIdPropertyKey: return object->as()->stateToId(); case StateTransitionBase::flagsPropertyKey: @@ -1304,6 +2037,8 @@ class CoreRegistry return object->as()->interpolationType(); case StateTransitionBase::interpolatorIdPropertyKey: return object->as()->interpolatorId(); + case StateTransitionBase::randomWeightPropertyKey: + return object->as()->randomWeight(); case StateMachineFireEventBase::eventIdPropertyKey: return object->as()->eventId(); case StateMachineFireEventBase::occursValuePropertyKey: @@ -1322,6 +2057,8 @@ class CoreRegistry return object->as()->easingValue(); case BlendState1DBase::inputIdPropertyKey: return object->as()->inputId(); + case TransitionValueEnumComparatorBase::valuePropertyKey: + return object->as()->value(); case BlendStateTransitionBase::exitBlendAnimationIdPropertyKey: return object->as()->exitBlendAnimationId(); case StrokeBase::capPropertyKey: @@ -1344,8 +2081,12 @@ class CoreRegistry return object->as()->assetId(); case DrawRulesBase::drawTargetIdPropertyKey: return object->as()->drawTargetId(); + case LayoutComponentBase::styleIdPropertyKey: + return object->as()->styleId(); case ArtboardBase::defaultStateMachineIdPropertyKey: return object->as()->defaultStateMachineId(); + case ArtboardBase::viewModelIdPropertyKey: + return object->as()->viewModelId(); case JoystickBase::xIdPropertyKey: return object->as()->xId(); case JoystickBase::yIdPropertyKey: @@ -1356,6 +2097,22 @@ class CoreRegistry return object->as()->handleSourceId(); case OpenUrlEventBase::targetValuePropertyKey: return object->as()->targetValue(); + case DataBindBase::propertyKeyPropertyKey: + return object->as()->propertyKey(); + case DataBindBase::flagsPropertyKey: + return object->as()->flags(); + case DataBindBase::converterIdPropertyKey: + return object->as()->converterId(); + case DataConverterGroupItemBase::converterIdPropertyKey: + return object->as()->converterId(); + case DataConverterRounderBase::decimalsPropertyKey: + return object->as()->decimals(); + case DataConverterOperationBase::operationTypePropertyKey: + return object->as()->operationType(); + case BindablePropertyEnumBase::propertyValuePropertyKey: + return object->as()->propertyValue(); + case NestedArtboardLeafBase::fitPropertyKey: + return object->as()->fit(); case WeightBase::valuesPropertyKey: return object->as()->values(); case WeightBase::indicesPropertyKey: @@ -1407,10 +2164,70 @@ class CoreRegistry } return 0; } + static int getColor(Core* object, int propertyKey) + { + switch (propertyKey) + { + case ViewModelInstanceColorBase::propertyValuePropertyKey: + return object->as()->propertyValue(); + case KeyFrameColorBase::valuePropertyKey: + return object->as()->value(); + case TransitionValueColorComparatorBase::valuePropertyKey: + return object->as()->value(); + case SolidColorBase::colorValuePropertyKey: + return object->as()->colorValue(); + case GradientStopBase::colorValuePropertyKey: + return object->as()->colorValue(); + case BindablePropertyColorBase::propertyValuePropertyKey: + return object->as()->propertyValue(); + } + return 0; + } + static std::string getString(Core* object, int propertyKey) + { + switch (propertyKey) + { + case ViewModelComponentBase::namePropertyKey: + return object->as()->name(); + case ViewModelInstanceStringBase::propertyValuePropertyKey: + return object->as()->propertyValue(); + case ComponentBase::namePropertyKey: + return object->as()->name(); + case DataEnumValueBase::keyPropertyKey: + return object->as()->key(); + case DataEnumValueBase::valuePropertyKey: + return object->as()->value(); + case AnimationBase::namePropertyKey: + return object->as()->name(); + case StateMachineComponentBase::namePropertyKey: + return object->as()->name(); + case KeyFrameStringBase::valuePropertyKey: + return object->as()->value(); + case TransitionValueStringComparatorBase::valuePropertyKey: + return object->as()->value(); + case OpenUrlEventBase::urlPropertyKey: + return object->as()->url(); + case DataConverterBase::namePropertyKey: + return object->as()->name(); + case BindablePropertyStringBase::propertyValuePropertyKey: + return object->as()->propertyValue(); + case TextValueRunBase::textPropertyKey: + return object->as()->text(); + case CustomPropertyStringBase::propertyValuePropertyKey: + return object->as()->propertyValue(); + case AssetBase::namePropertyKey: + return object->as()->name(); + case FileAssetBase::cdnBaseUrlPropertyKey: + return object->as()->cdnBaseUrl(); + } + return ""; + } static float getDouble(Core* object, int propertyKey) { switch (propertyKey) { + case ViewModelInstanceNumberBase::propertyValuePropertyKey: + return object->as()->propertyValue(); case CustomPropertyNumberBase::propertyValuePropertyKey: return object->as()->propertyValue(); case ConstraintBase::strengthPropertyKey: @@ -1428,607 +2245,1548 @@ class CoreRegistry case TransformComponentConstraintYBase::minValueYPropertyKey: return object->as()->minValueY(); case TransformComponentConstraintYBase::maxValueYPropertyKey: - return object->as()->maxValueY(); + return object->as()->maxValueY(); + case FollowPathConstraintBase::distancePropertyKey: + return object->as()->distance(); + case TransformConstraintBase::originXPropertyKey: + return object->as()->originX(); + case TransformConstraintBase::originYPropertyKey: + return object->as()->originY(); + case WorldTransformComponentBase::opacityPropertyKey: + return object->as()->opacity(); + case TransformComponentBase::rotationPropertyKey: + return object->as()->rotation(); + case TransformComponentBase::scaleXPropertyKey: + return object->as()->scaleX(); + case TransformComponentBase::scaleYPropertyKey: + return object->as()->scaleY(); + case NodeBase::xPropertyKey: + case NodeBase::xArtboardPropertyKey: + return object->as()->x(); + case NodeBase::yPropertyKey: + case NodeBase::yArtboardPropertyKey: + return object->as()->y(); + case NestedArtboardLayoutBase::instanceWidthPropertyKey: + return object->as()->instanceWidth(); + case NestedArtboardLayoutBase::instanceHeightPropertyKey: + return object->as()->instanceHeight(); + case AxisBase::offsetPropertyKey: + return object->as()->offset(); + case LayoutComponentStyleBase::gapHorizontalPropertyKey: + return object->as()->gapHorizontal(); + case LayoutComponentStyleBase::gapVerticalPropertyKey: + return object->as()->gapVertical(); + case LayoutComponentStyleBase::maxWidthPropertyKey: + return object->as()->maxWidth(); + case LayoutComponentStyleBase::maxHeightPropertyKey: + return object->as()->maxHeight(); + case LayoutComponentStyleBase::minWidthPropertyKey: + return object->as()->minWidth(); + case LayoutComponentStyleBase::minHeightPropertyKey: + return object->as()->minHeight(); + case LayoutComponentStyleBase::borderLeftPropertyKey: + return object->as()->borderLeft(); + case LayoutComponentStyleBase::borderRightPropertyKey: + return object->as()->borderRight(); + case LayoutComponentStyleBase::borderTopPropertyKey: + return object->as()->borderTop(); + case LayoutComponentStyleBase::borderBottomPropertyKey: + return object->as()->borderBottom(); + case LayoutComponentStyleBase::marginLeftPropertyKey: + return object->as()->marginLeft(); + case LayoutComponentStyleBase::marginRightPropertyKey: + return object->as()->marginRight(); + case LayoutComponentStyleBase::marginTopPropertyKey: + return object->as()->marginTop(); + case LayoutComponentStyleBase::marginBottomPropertyKey: + return object->as()->marginBottom(); + case LayoutComponentStyleBase::paddingLeftPropertyKey: + return object->as()->paddingLeft(); + case LayoutComponentStyleBase::paddingRightPropertyKey: + return object->as()->paddingRight(); + case LayoutComponentStyleBase::paddingTopPropertyKey: + return object->as()->paddingTop(); + case LayoutComponentStyleBase::paddingBottomPropertyKey: + return object->as()->paddingBottom(); + case LayoutComponentStyleBase::positionLeftPropertyKey: + return object->as()->positionLeft(); + case LayoutComponentStyleBase::positionRightPropertyKey: + return object->as()->positionRight(); + case LayoutComponentStyleBase::positionTopPropertyKey: + return object->as()->positionTop(); + case LayoutComponentStyleBase::positionBottomPropertyKey: + return object->as()->positionBottom(); + case LayoutComponentStyleBase::flexPropertyKey: + return object->as()->flex(); + case LayoutComponentStyleBase::flexGrowPropertyKey: + return object->as()->flexGrow(); + case LayoutComponentStyleBase::flexShrinkPropertyKey: + return object->as()->flexShrink(); + case LayoutComponentStyleBase::flexBasisPropertyKey: + return object->as()->flexBasis(); + case LayoutComponentStyleBase::aspectRatioPropertyKey: + return object->as()->aspectRatio(); + case LayoutComponentStyleBase::interpolationTimePropertyKey: + return object->as()->interpolationTime(); + case LayoutComponentStyleBase::cornerRadiusTLPropertyKey: + return object->as()->cornerRadiusTL(); + case LayoutComponentStyleBase::cornerRadiusTRPropertyKey: + return object->as()->cornerRadiusTR(); + case LayoutComponentStyleBase::cornerRadiusBLPropertyKey: + return object->as()->cornerRadiusBL(); + case LayoutComponentStyleBase::cornerRadiusBRPropertyKey: + return object->as()->cornerRadiusBR(); + case NestedLinearAnimationBase::mixPropertyKey: + return object->as()->mix(); + case NestedSimpleAnimationBase::speedPropertyKey: + return object->as()->speed(); + case AdvanceableStateBase::speedPropertyKey: + return object->as()->speed(); + case BlendAnimationDirectBase::mixValuePropertyKey: + return object->as()->mixValue(); + case StateMachineNumberBase::valuePropertyKey: + return object->as()->value(); + case CubicInterpolatorBase::x1PropertyKey: + return object->as()->x1(); + case CubicInterpolatorBase::y1PropertyKey: + return object->as()->y1(); + case CubicInterpolatorBase::x2PropertyKey: + return object->as()->x2(); + case CubicInterpolatorBase::y2PropertyKey: + return object->as()->y2(); + case TransitionNumberConditionBase::valuePropertyKey: + return object->as()->value(); + case CubicInterpolatorComponentBase::x1PropertyKey: + return object->as()->x1(); + case CubicInterpolatorComponentBase::y1PropertyKey: + return object->as()->y1(); + case CubicInterpolatorComponentBase::x2PropertyKey: + return object->as()->x2(); + case CubicInterpolatorComponentBase::y2PropertyKey: + return object->as()->y2(); + case ListenerNumberChangeBase::valuePropertyKey: + return object->as()->value(); + case KeyFrameDoubleBase::valuePropertyKey: + return object->as()->value(); + case LinearAnimationBase::speedPropertyKey: + return object->as()->speed(); + case TransitionValueNumberComparatorBase::valuePropertyKey: + return object->as()->value(); + case ElasticInterpolatorBase::amplitudePropertyKey: + return object->as()->amplitude(); + case ElasticInterpolatorBase::periodPropertyKey: + return object->as()->period(); + case NestedNumberBase::nestedValuePropertyKey: + return object->as()->nestedValue(); + case NestedRemapAnimationBase::timePropertyKey: + return object->as()->time(); + case BlendAnimation1DBase::valuePropertyKey: + return object->as()->value(); + case LinearGradientBase::startXPropertyKey: + return object->as()->startX(); + case LinearGradientBase::startYPropertyKey: + return object->as()->startY(); + case LinearGradientBase::endXPropertyKey: + return object->as()->endX(); + case LinearGradientBase::endYPropertyKey: + return object->as()->endY(); + case LinearGradientBase::opacityPropertyKey: + return object->as()->opacity(); + case StrokeBase::thicknessPropertyKey: + return object->as()->thickness(); + case GradientStopBase::positionPropertyKey: + return object->as()->position(); + case TrimPathBase::startPropertyKey: + return object->as()->start(); + case TrimPathBase::endPropertyKey: + return object->as()->end(); + case TrimPathBase::offsetPropertyKey: + return object->as()->offset(); + case VertexBase::xPropertyKey: + return object->as()->x(); + case VertexBase::yPropertyKey: + return object->as()->y(); + case MeshVertexBase::uPropertyKey: + return object->as()->u(); + case MeshVertexBase::vPropertyKey: + return object->as()->v(); + case StraightVertexBase::radiusPropertyKey: + return object->as()->radius(); + case CubicAsymmetricVertexBase::rotationPropertyKey: + return object->as()->rotation(); + case CubicAsymmetricVertexBase::inDistancePropertyKey: + return object->as()->inDistance(); + case CubicAsymmetricVertexBase::outDistancePropertyKey: + return object->as()->outDistance(); + case ParametricPathBase::widthPropertyKey: + return object->as()->width(); + case ParametricPathBase::heightPropertyKey: + return object->as()->height(); + case ParametricPathBase::originXPropertyKey: + return object->as()->originX(); + case ParametricPathBase::originYPropertyKey: + return object->as()->originY(); + case RectangleBase::cornerRadiusTLPropertyKey: + return object->as()->cornerRadiusTL(); + case RectangleBase::cornerRadiusTRPropertyKey: + return object->as()->cornerRadiusTR(); + case RectangleBase::cornerRadiusBLPropertyKey: + return object->as()->cornerRadiusBL(); + case RectangleBase::cornerRadiusBRPropertyKey: + return object->as()->cornerRadiusBR(); + case CubicMirroredVertexBase::rotationPropertyKey: + return object->as()->rotation(); + case CubicMirroredVertexBase::distancePropertyKey: + return object->as()->distance(); + case PolygonBase::cornerRadiusPropertyKey: + return object->as()->cornerRadius(); + case StarBase::innerRadiusPropertyKey: + return object->as()->innerRadius(); + case ImageBase::originXPropertyKey: + return object->as()->originX(); + case ImageBase::originYPropertyKey: + return object->as()->originY(); + case CubicDetachedVertexBase::inRotationPropertyKey: + return object->as()->inRotation(); + case CubicDetachedVertexBase::inDistancePropertyKey: + return object->as()->inDistance(); + case CubicDetachedVertexBase::outRotationPropertyKey: + return object->as()->outRotation(); + case CubicDetachedVertexBase::outDistancePropertyKey: + return object->as()->outDistance(); + case LayoutComponentBase::widthPropertyKey: + return object->as()->width(); + case LayoutComponentBase::heightPropertyKey: + return object->as()->height(); + case ArtboardBase::originXPropertyKey: + return object->as()->originX(); + case ArtboardBase::originYPropertyKey: + return object->as()->originY(); + case JoystickBase::xPropertyKey: + return object->as()->x(); + case JoystickBase::yPropertyKey: + return object->as()->y(); + case JoystickBase::posXPropertyKey: + return object->as()->posX(); + case JoystickBase::posYPropertyKey: + return object->as()->posY(); + case JoystickBase::originXPropertyKey: + return object->as()->originX(); + case JoystickBase::originYPropertyKey: + return object->as()->originY(); + case JoystickBase::widthPropertyKey: + return object->as()->width(); + case JoystickBase::heightPropertyKey: + return object->as()->height(); + case DataConverterOperationBase::valuePropertyKey: + return object->as()->value(); + case BindablePropertyNumberBase::propertyValuePropertyKey: + return object->as()->propertyValue(); + case NestedArtboardLeafBase::alignmentXPropertyKey: + return object->as()->alignmentX(); + case NestedArtboardLeafBase::alignmentYPropertyKey: + return object->as()->alignmentY(); + case BoneBase::lengthPropertyKey: + return object->as()->length(); + case RootBoneBase::xPropertyKey: + return object->as()->x(); + case RootBoneBase::yPropertyKey: + return object->as()->y(); + case SkinBase::xxPropertyKey: + return object->as()->xx(); + case SkinBase::yxPropertyKey: + return object->as()->yx(); + case SkinBase::xyPropertyKey: + return object->as()->xy(); + case SkinBase::yyPropertyKey: + return object->as()->yy(); + case SkinBase::txPropertyKey: + return object->as()->tx(); + case SkinBase::tyPropertyKey: + return object->as()->ty(); + case TendonBase::xxPropertyKey: + return object->as()->xx(); + case TendonBase::yxPropertyKey: + return object->as()->yx(); + case TendonBase::xyPropertyKey: + return object->as()->xy(); + case TendonBase::yyPropertyKey: + return object->as()->yy(); + case TendonBase::txPropertyKey: + return object->as()->tx(); + case TendonBase::tyPropertyKey: + return object->as()->ty(); + case TextModifierRangeBase::modifyFromPropertyKey: + return object->as()->modifyFrom(); + case TextModifierRangeBase::modifyToPropertyKey: + return object->as()->modifyTo(); + case TextModifierRangeBase::strengthPropertyKey: + return object->as()->strength(); + case TextModifierRangeBase::falloffFromPropertyKey: + return object->as()->falloffFrom(); + case TextModifierRangeBase::falloffToPropertyKey: + return object->as()->falloffTo(); + case TextModifierRangeBase::offsetPropertyKey: + return object->as()->offset(); + case TextVariationModifierBase::axisValuePropertyKey: + return object->as()->axisValue(); + case TextModifierGroupBase::originXPropertyKey: + return object->as()->originX(); + case TextModifierGroupBase::originYPropertyKey: + return object->as()->originY(); + case TextModifierGroupBase::opacityPropertyKey: + return object->as()->opacity(); + case TextModifierGroupBase::xPropertyKey: + return object->as()->x(); + case TextModifierGroupBase::yPropertyKey: + return object->as()->y(); + case TextModifierGroupBase::rotationPropertyKey: + return object->as()->rotation(); + case TextModifierGroupBase::scaleXPropertyKey: + return object->as()->scaleX(); + case TextModifierGroupBase::scaleYPropertyKey: + return object->as()->scaleY(); + case TextStyleBase::fontSizePropertyKey: + return object->as()->fontSize(); + case TextStyleBase::lineHeightPropertyKey: + return object->as()->lineHeight(); + case TextStyleBase::letterSpacingPropertyKey: + return object->as()->letterSpacing(); + case TextStyleAxisBase::axisValuePropertyKey: + return object->as()->axisValue(); + case TextBase::widthPropertyKey: + return object->as()->width(); + case TextBase::heightPropertyKey: + return object->as()->height(); + case TextBase::originXPropertyKey: + return object->as()->originX(); + case TextBase::originYPropertyKey: + return object->as()->originY(); + case TextBase::paragraphSpacingPropertyKey: + return object->as()->paragraphSpacing(); + case DrawableAssetBase::heightPropertyKey: + return object->as()->height(); + case DrawableAssetBase::widthPropertyKey: + return object->as()->width(); + case ExportAudioBase::volumePropertyKey: + return object->as()->volume(); + } + return 0.0f; + } + static int propertyFieldId(int propertyKey) + { + switch (propertyKey) + { + case ViewModelInstanceListItemBase::useLinkedArtboardPropertyKey: + case ViewModelInstanceBooleanBase::propertyValuePropertyKey: + case TransformComponentConstraintBase::offsetPropertyKey: + case TransformComponentConstraintBase::doesCopyPropertyKey: + case TransformComponentConstraintBase::minPropertyKey: + case TransformComponentConstraintBase::maxPropertyKey: + case TransformComponentConstraintYBase::doesCopyYPropertyKey: + case TransformComponentConstraintYBase::minYPropertyKey: + case TransformComponentConstraintYBase::maxYPropertyKey: + case IKConstraintBase::invertDirectionPropertyKey: + case FollowPathConstraintBase::orientPropertyKey: + case FollowPathConstraintBase::offsetPropertyKey: + case AxisBase::normalizedPropertyKey: + case LayoutComponentStyleBase::intrinsicallySizedValuePropertyKey: + case LayoutComponentStyleBase::linkCornerRadiusPropertyKey: + case NestedSimpleAnimationBase::isPlayingPropertyKey: + case KeyFrameBoolBase::valuePropertyKey: + case ListenerAlignTargetBase::preserveOffsetPropertyKey: + case TransitionValueBooleanComparatorBase::valuePropertyKey: + case NestedBoolBase::nestedValuePropertyKey: + case LinearAnimationBase::enableWorkAreaPropertyKey: + case LinearAnimationBase::quantizePropertyKey: + case StateMachineBoolBase::valuePropertyKey: + case ShapePaintBase::isVisiblePropertyKey: + case StrokeBase::transformAffectsStrokePropertyKey: + case PointsPathBase::isClosedPropertyKey: + case RectangleBase::linkCornerRadiusPropertyKey: + case ClippingShapeBase::isVisiblePropertyKey: + case CustomPropertyBooleanBase::propertyValuePropertyKey: + case LayoutComponentBase::clipPropertyKey: + case BindablePropertyBooleanBase::propertyValuePropertyKey: + case TextModifierRangeBase::clampPropertyKey: + return CoreBoolType::id; + case ViewModelInstanceListItemBase::viewModelIdPropertyKey: + case ViewModelInstanceListItemBase::viewModelInstanceIdPropertyKey: + case ViewModelInstanceListItemBase::artboardIdPropertyKey: + case ViewModelInstanceValueBase::viewModelPropertyIdPropertyKey: + case ViewModelInstanceEnumBase::propertyValuePropertyKey: + case ViewModelBase::defaultInstanceIdPropertyKey: + case ViewModelPropertyViewModelBase::viewModelReferenceIdPropertyKey: + case ComponentBase::parentIdPropertyKey: + case ViewModelInstanceBase::viewModelIdPropertyKey: + case ViewModelPropertyEnumBase::enumIdPropertyKey: + case ViewModelInstanceViewModelBase::propertyValuePropertyKey: + case DrawTargetBase::drawableIdPropertyKey: + case DrawTargetBase::placementValuePropertyKey: + case TargetedConstraintBase::targetIdPropertyKey: + case DistanceConstraintBase::modeValuePropertyKey: + case TransformSpaceConstraintBase::sourceSpaceValuePropertyKey: + case TransformSpaceConstraintBase::destSpaceValuePropertyKey: + case TransformComponentConstraintBase::minMaxSpaceValuePropertyKey: + case IKConstraintBase::parentBoneCountPropertyKey: + case DrawableBase::blendModeValuePropertyKey: + case DrawableBase::drawableFlagsPropertyKey: + case NestedArtboardBase::artboardIdPropertyKey: + case NestedAnimationBase::animationIdPropertyKey: + case SoloBase::activeComponentIdPropertyKey: + case NestedArtboardLayoutBase::instanceWidthUnitsValuePropertyKey: + case NestedArtboardLayoutBase::instanceHeightUnitsValuePropertyKey: + case NestedArtboardLayoutBase::instanceWidthScaleTypePropertyKey: + case NestedArtboardLayoutBase::instanceHeightScaleTypePropertyKey: + case NSlicerTileModeBase::patchIndexPropertyKey: + case NSlicerTileModeBase::stylePropertyKey: + case LayoutComponentStyleBase::layoutWidthScaleTypePropertyKey: + case LayoutComponentStyleBase::layoutHeightScaleTypePropertyKey: + case LayoutComponentStyleBase::layoutAlignmentTypePropertyKey: + case LayoutComponentStyleBase::animationStyleTypePropertyKey: + case LayoutComponentStyleBase::interpolationTypePropertyKey: + case LayoutComponentStyleBase::interpolatorIdPropertyKey: + case LayoutComponentStyleBase::displayValuePropertyKey: + case LayoutComponentStyleBase::positionTypeValuePropertyKey: + case LayoutComponentStyleBase::flexDirectionValuePropertyKey: + case LayoutComponentStyleBase::directionValuePropertyKey: + case LayoutComponentStyleBase::alignContentValuePropertyKey: + case LayoutComponentStyleBase::alignItemsValuePropertyKey: + case LayoutComponentStyleBase::alignSelfValuePropertyKey: + case LayoutComponentStyleBase::justifyContentValuePropertyKey: + case LayoutComponentStyleBase::flexWrapValuePropertyKey: + case LayoutComponentStyleBase::overflowValuePropertyKey: + case LayoutComponentStyleBase::widthUnitsValuePropertyKey: + case LayoutComponentStyleBase::heightUnitsValuePropertyKey: + case LayoutComponentStyleBase::borderLeftUnitsValuePropertyKey: + case LayoutComponentStyleBase::borderRightUnitsValuePropertyKey: + case LayoutComponentStyleBase::borderTopUnitsValuePropertyKey: + case LayoutComponentStyleBase::borderBottomUnitsValuePropertyKey: + case LayoutComponentStyleBase::marginLeftUnitsValuePropertyKey: + case LayoutComponentStyleBase::marginRightUnitsValuePropertyKey: + case LayoutComponentStyleBase::marginTopUnitsValuePropertyKey: + case LayoutComponentStyleBase::marginBottomUnitsValuePropertyKey: + case LayoutComponentStyleBase::paddingLeftUnitsValuePropertyKey: + case LayoutComponentStyleBase::paddingRightUnitsValuePropertyKey: + case LayoutComponentStyleBase::paddingTopUnitsValuePropertyKey: + case LayoutComponentStyleBase::paddingBottomUnitsValuePropertyKey: + case LayoutComponentStyleBase::positionLeftUnitsValuePropertyKey: + case LayoutComponentStyleBase::positionRightUnitsValuePropertyKey: + case LayoutComponentStyleBase::positionTopUnitsValuePropertyKey: + case LayoutComponentStyleBase::positionBottomUnitsValuePropertyKey: + case LayoutComponentStyleBase::gapHorizontalUnitsValuePropertyKey: + case LayoutComponentStyleBase::gapVerticalUnitsValuePropertyKey: + case LayoutComponentStyleBase::minWidthUnitsValuePropertyKey: + case LayoutComponentStyleBase::minHeightUnitsValuePropertyKey: + case LayoutComponentStyleBase::maxWidthUnitsValuePropertyKey: + case LayoutComponentStyleBase::maxHeightUnitsValuePropertyKey: + case ListenerFireEventBase::eventIdPropertyKey: + case LayerStateBase::flagsPropertyKey: + case KeyFrameBase::framePropertyKey: + case InterpolatingKeyFrameBase::interpolationTypePropertyKey: + case InterpolatingKeyFrameBase::interpolatorIdPropertyKey: + case KeyFrameUintBase::valuePropertyKey: + case ListenerInputChangeBase::inputIdPropertyKey: + case ListenerInputChangeBase::nestedInputIdPropertyKey: + case AnimationStateBase::animationIdPropertyKey: + case NestedInputBase::inputIdPropertyKey: + case KeyedObjectBase::objectIdPropertyKey: + case BlendAnimationBase::animationIdPropertyKey: + case BlendAnimationDirectBase::inputIdPropertyKey: + case BlendAnimationDirectBase::blendSourcePropertyKey: + case TransitionInputConditionBase::inputIdPropertyKey: + case KeyedPropertyBase::propertyKeyPropertyKey: + case StateMachineListenerBase::targetIdPropertyKey: + case StateMachineListenerBase::listenerTypeValuePropertyKey: + case StateMachineListenerBase::eventIdPropertyKey: + case TransitionPropertyArtboardComparatorBase::propertyTypePropertyKey: + case KeyFrameIdBase::valuePropertyKey: + case ListenerBoolChangeBase::valuePropertyKey: + case ListenerAlignTargetBase::targetIdPropertyKey: + case TransitionValueConditionBase::opValuePropertyKey: + case TransitionViewModelConditionBase::leftComparatorIdPropertyKey: + case TransitionViewModelConditionBase::rightComparatorIdPropertyKey: + case TransitionViewModelConditionBase::opValuePropertyKey: + case StateTransitionBase::stateToIdPropertyKey: + case StateTransitionBase::flagsPropertyKey: + case StateTransitionBase::durationPropertyKey: + case StateTransitionBase::exitTimePropertyKey: + case StateTransitionBase::interpolationTypePropertyKey: + case StateTransitionBase::interpolatorIdPropertyKey: + case StateTransitionBase::randomWeightPropertyKey: + case StateMachineFireEventBase::eventIdPropertyKey: + case StateMachineFireEventBase::occursValuePropertyKey: + case LinearAnimationBase::fpsPropertyKey: + case LinearAnimationBase::durationPropertyKey: + case LinearAnimationBase::loopValuePropertyKey: + case LinearAnimationBase::workStartPropertyKey: + case LinearAnimationBase::workEndPropertyKey: + case ElasticInterpolatorBase::easingValuePropertyKey: + case BlendState1DBase::inputIdPropertyKey: + case TransitionValueEnumComparatorBase::valuePropertyKey: + case BlendStateTransitionBase::exitBlendAnimationIdPropertyKey: + case StrokeBase::capPropertyKey: + case StrokeBase::joinPropertyKey: + case TrimPathBase::modeValuePropertyKey: + case FillBase::fillRulePropertyKey: + case PathBase::pathFlagsPropertyKey: + case ClippingShapeBase::sourceIdPropertyKey: + case ClippingShapeBase::fillRulePropertyKey: + case PolygonBase::pointsPropertyKey: + case ImageBase::assetIdPropertyKey: + case DrawRulesBase::drawTargetIdPropertyKey: + case LayoutComponentBase::styleIdPropertyKey: + case ArtboardBase::defaultStateMachineIdPropertyKey: + case ArtboardBase::viewModelIdPropertyKey: + case JoystickBase::xIdPropertyKey: + case JoystickBase::yIdPropertyKey: + case JoystickBase::joystickFlagsPropertyKey: + case JoystickBase::handleSourceIdPropertyKey: + case OpenUrlEventBase::targetValuePropertyKey: + case DataBindBase::propertyKeyPropertyKey: + case DataBindBase::flagsPropertyKey: + case DataBindBase::converterIdPropertyKey: + case DataConverterGroupItemBase::converterIdPropertyKey: + case DataConverterRounderBase::decimalsPropertyKey: + case DataConverterOperationBase::operationTypePropertyKey: + case BindablePropertyEnumBase::propertyValuePropertyKey: + case NestedArtboardLeafBase::fitPropertyKey: + case WeightBase::valuesPropertyKey: + case WeightBase::indicesPropertyKey: + case TendonBase::boneIdPropertyKey: + case CubicWeightBase::inValuesPropertyKey: + case CubicWeightBase::inIndicesPropertyKey: + case CubicWeightBase::outValuesPropertyKey: + case CubicWeightBase::outIndicesPropertyKey: + case TextModifierRangeBase::unitsValuePropertyKey: + case TextModifierRangeBase::typeValuePropertyKey: + case TextModifierRangeBase::modeValuePropertyKey: + case TextModifierRangeBase::runIdPropertyKey: + case TextStyleFeatureBase::tagPropertyKey: + case TextStyleFeatureBase::featureValuePropertyKey: + case TextVariationModifierBase::axisTagPropertyKey: + case TextModifierGroupBase::modifierFlagsPropertyKey: + case TextStyleBase::fontAssetIdPropertyKey: + case TextStyleAxisBase::tagPropertyKey: + case TextBase::alignValuePropertyKey: + case TextBase::sizingValuePropertyKey: + case TextBase::overflowValuePropertyKey: + case TextBase::originValuePropertyKey: + case TextValueRunBase::styleIdPropertyKey: + case FileAssetBase::assetIdPropertyKey: + case AudioEventBase::assetIdPropertyKey: + return CoreUintType::id; + case ViewModelInstanceColorBase::propertyValuePropertyKey: + case KeyFrameColorBase::valuePropertyKey: + case TransitionValueColorComparatorBase::valuePropertyKey: + case SolidColorBase::colorValuePropertyKey: + case GradientStopBase::colorValuePropertyKey: + case BindablePropertyColorBase::propertyValuePropertyKey: + return CoreColorType::id; + case ViewModelComponentBase::namePropertyKey: + case ViewModelInstanceStringBase::propertyValuePropertyKey: + case ComponentBase::namePropertyKey: + case DataEnumValueBase::keyPropertyKey: + case DataEnumValueBase::valuePropertyKey: + case AnimationBase::namePropertyKey: + case StateMachineComponentBase::namePropertyKey: + case KeyFrameStringBase::valuePropertyKey: + case TransitionValueStringComparatorBase::valuePropertyKey: + case OpenUrlEventBase::urlPropertyKey: + case DataConverterBase::namePropertyKey: + case BindablePropertyStringBase::propertyValuePropertyKey: + case TextValueRunBase::textPropertyKey: + case CustomPropertyStringBase::propertyValuePropertyKey: + case AssetBase::namePropertyKey: + case FileAssetBase::cdnBaseUrlPropertyKey: + return CoreStringType::id; + case ViewModelInstanceNumberBase::propertyValuePropertyKey: + case CustomPropertyNumberBase::propertyValuePropertyKey: + case ConstraintBase::strengthPropertyKey: + case DistanceConstraintBase::distancePropertyKey: + case TransformComponentConstraintBase::copyFactorPropertyKey: + case TransformComponentConstraintBase::minValuePropertyKey: + case TransformComponentConstraintBase::maxValuePropertyKey: + case TransformComponentConstraintYBase::copyFactorYPropertyKey: + case TransformComponentConstraintYBase::minValueYPropertyKey: + case TransformComponentConstraintYBase::maxValueYPropertyKey: case FollowPathConstraintBase::distancePropertyKey: - return object->as()->distance(); case TransformConstraintBase::originXPropertyKey: - return object->as()->originX(); case TransformConstraintBase::originYPropertyKey: - return object->as()->originY(); case WorldTransformComponentBase::opacityPropertyKey: - return object->as()->opacity(); case TransformComponentBase::rotationPropertyKey: - return object->as()->rotation(); case TransformComponentBase::scaleXPropertyKey: - return object->as()->scaleX(); case TransformComponentBase::scaleYPropertyKey: - return object->as()->scaleY(); case NodeBase::xPropertyKey: - return object->as()->x(); + case NodeBase::xArtboardPropertyKey: case NodeBase::yPropertyKey: - return object->as()->y(); + case NodeBase::yArtboardPropertyKey: + case NestedArtboardLayoutBase::instanceWidthPropertyKey: + case NestedArtboardLayoutBase::instanceHeightPropertyKey: + case AxisBase::offsetPropertyKey: + case LayoutComponentStyleBase::gapHorizontalPropertyKey: + case LayoutComponentStyleBase::gapVerticalPropertyKey: + case LayoutComponentStyleBase::maxWidthPropertyKey: + case LayoutComponentStyleBase::maxHeightPropertyKey: + case LayoutComponentStyleBase::minWidthPropertyKey: + case LayoutComponentStyleBase::minHeightPropertyKey: + case LayoutComponentStyleBase::borderLeftPropertyKey: + case LayoutComponentStyleBase::borderRightPropertyKey: + case LayoutComponentStyleBase::borderTopPropertyKey: + case LayoutComponentStyleBase::borderBottomPropertyKey: + case LayoutComponentStyleBase::marginLeftPropertyKey: + case LayoutComponentStyleBase::marginRightPropertyKey: + case LayoutComponentStyleBase::marginTopPropertyKey: + case LayoutComponentStyleBase::marginBottomPropertyKey: + case LayoutComponentStyleBase::paddingLeftPropertyKey: + case LayoutComponentStyleBase::paddingRightPropertyKey: + case LayoutComponentStyleBase::paddingTopPropertyKey: + case LayoutComponentStyleBase::paddingBottomPropertyKey: + case LayoutComponentStyleBase::positionLeftPropertyKey: + case LayoutComponentStyleBase::positionRightPropertyKey: + case LayoutComponentStyleBase::positionTopPropertyKey: + case LayoutComponentStyleBase::positionBottomPropertyKey: + case LayoutComponentStyleBase::flexPropertyKey: + case LayoutComponentStyleBase::flexGrowPropertyKey: + case LayoutComponentStyleBase::flexShrinkPropertyKey: + case LayoutComponentStyleBase::flexBasisPropertyKey: + case LayoutComponentStyleBase::aspectRatioPropertyKey: + case LayoutComponentStyleBase::interpolationTimePropertyKey: + case LayoutComponentStyleBase::cornerRadiusTLPropertyKey: + case LayoutComponentStyleBase::cornerRadiusTRPropertyKey: + case LayoutComponentStyleBase::cornerRadiusBLPropertyKey: + case LayoutComponentStyleBase::cornerRadiusBRPropertyKey: case NestedLinearAnimationBase::mixPropertyKey: - return object->as()->mix(); case NestedSimpleAnimationBase::speedPropertyKey: - return object->as()->speed(); case AdvanceableStateBase::speedPropertyKey: - return object->as()->speed(); case BlendAnimationDirectBase::mixValuePropertyKey: - return object->as()->mixValue(); case StateMachineNumberBase::valuePropertyKey: - return object->as()->value(); case CubicInterpolatorBase::x1PropertyKey: - return object->as()->x1(); case CubicInterpolatorBase::y1PropertyKey: - return object->as()->y1(); case CubicInterpolatorBase::x2PropertyKey: - return object->as()->x2(); case CubicInterpolatorBase::y2PropertyKey: - return object->as()->y2(); case TransitionNumberConditionBase::valuePropertyKey: - return object->as()->value(); case CubicInterpolatorComponentBase::x1PropertyKey: - return object->as()->x1(); case CubicInterpolatorComponentBase::y1PropertyKey: - return object->as()->y1(); case CubicInterpolatorComponentBase::x2PropertyKey: - return object->as()->x2(); case CubicInterpolatorComponentBase::y2PropertyKey: - return object->as()->y2(); case ListenerNumberChangeBase::valuePropertyKey: - return object->as()->value(); case KeyFrameDoubleBase::valuePropertyKey: - return object->as()->value(); case LinearAnimationBase::speedPropertyKey: - return object->as()->speed(); + case TransitionValueNumberComparatorBase::valuePropertyKey: case ElasticInterpolatorBase::amplitudePropertyKey: - return object->as()->amplitude(); case ElasticInterpolatorBase::periodPropertyKey: - return object->as()->period(); case NestedNumberBase::nestedValuePropertyKey: - return object->as()->nestedValue(); case NestedRemapAnimationBase::timePropertyKey: - return object->as()->time(); case BlendAnimation1DBase::valuePropertyKey: - return object->as()->value(); case LinearGradientBase::startXPropertyKey: - return object->as()->startX(); case LinearGradientBase::startYPropertyKey: - return object->as()->startY(); case LinearGradientBase::endXPropertyKey: - return object->as()->endX(); case LinearGradientBase::endYPropertyKey: - return object->as()->endY(); case LinearGradientBase::opacityPropertyKey: - return object->as()->opacity(); case StrokeBase::thicknessPropertyKey: - return object->as()->thickness(); case GradientStopBase::positionPropertyKey: - return object->as()->position(); case TrimPathBase::startPropertyKey: - return object->as()->start(); case TrimPathBase::endPropertyKey: - return object->as()->end(); case TrimPathBase::offsetPropertyKey: - return object->as()->offset(); case VertexBase::xPropertyKey: - return object->as()->x(); case VertexBase::yPropertyKey: - return object->as()->y(); case MeshVertexBase::uPropertyKey: - return object->as()->u(); case MeshVertexBase::vPropertyKey: - return object->as()->v(); case StraightVertexBase::radiusPropertyKey: - return object->as()->radius(); case CubicAsymmetricVertexBase::rotationPropertyKey: - return object->as()->rotation(); case CubicAsymmetricVertexBase::inDistancePropertyKey: - return object->as()->inDistance(); case CubicAsymmetricVertexBase::outDistancePropertyKey: - return object->as()->outDistance(); case ParametricPathBase::widthPropertyKey: - return object->as()->width(); case ParametricPathBase::heightPropertyKey: - return object->as()->height(); case ParametricPathBase::originXPropertyKey: - return object->as()->originX(); case ParametricPathBase::originYPropertyKey: - return object->as()->originY(); case RectangleBase::cornerRadiusTLPropertyKey: - return object->as()->cornerRadiusTL(); case RectangleBase::cornerRadiusTRPropertyKey: - return object->as()->cornerRadiusTR(); case RectangleBase::cornerRadiusBLPropertyKey: - return object->as()->cornerRadiusBL(); case RectangleBase::cornerRadiusBRPropertyKey: - return object->as()->cornerRadiusBR(); case CubicMirroredVertexBase::rotationPropertyKey: - return object->as()->rotation(); case CubicMirroredVertexBase::distancePropertyKey: - return object->as()->distance(); case PolygonBase::cornerRadiusPropertyKey: - return object->as()->cornerRadius(); case StarBase::innerRadiusPropertyKey: - return object->as()->innerRadius(); case ImageBase::originXPropertyKey: - return object->as()->originX(); case ImageBase::originYPropertyKey: - return object->as()->originY(); case CubicDetachedVertexBase::inRotationPropertyKey: - return object->as()->inRotation(); case CubicDetachedVertexBase::inDistancePropertyKey: - return object->as()->inDistance(); case CubicDetachedVertexBase::outRotationPropertyKey: - return object->as()->outRotation(); case CubicDetachedVertexBase::outDistancePropertyKey: - return object->as()->outDistance(); - case ArtboardBase::widthPropertyKey: - return object->as()->width(); - case ArtboardBase::heightPropertyKey: - return object->as()->height(); - case ArtboardBase::xPropertyKey: - return object->as()->x(); - case ArtboardBase::yPropertyKey: - return object->as()->y(); + case LayoutComponentBase::widthPropertyKey: + case LayoutComponentBase::heightPropertyKey: case ArtboardBase::originXPropertyKey: - return object->as()->originX(); case ArtboardBase::originYPropertyKey: - return object->as()->originY(); case JoystickBase::xPropertyKey: - return object->as()->x(); case JoystickBase::yPropertyKey: - return object->as()->y(); case JoystickBase::posXPropertyKey: - return object->as()->posX(); case JoystickBase::posYPropertyKey: - return object->as()->posY(); case JoystickBase::originXPropertyKey: - return object->as()->originX(); case JoystickBase::originYPropertyKey: - return object->as()->originY(); case JoystickBase::widthPropertyKey: - return object->as()->width(); case JoystickBase::heightPropertyKey: - return object->as()->height(); + case DataConverterOperationBase::valuePropertyKey: + case BindablePropertyNumberBase::propertyValuePropertyKey: + case NestedArtboardLeafBase::alignmentXPropertyKey: + case NestedArtboardLeafBase::alignmentYPropertyKey: case BoneBase::lengthPropertyKey: - return object->as()->length(); case RootBoneBase::xPropertyKey: - return object->as()->x(); case RootBoneBase::yPropertyKey: - return object->as()->y(); case SkinBase::xxPropertyKey: - return object->as()->xx(); case SkinBase::yxPropertyKey: - return object->as()->yx(); case SkinBase::xyPropertyKey: - return object->as()->xy(); case SkinBase::yyPropertyKey: - return object->as()->yy(); case SkinBase::txPropertyKey: - return object->as()->tx(); case SkinBase::tyPropertyKey: - return object->as()->ty(); case TendonBase::xxPropertyKey: - return object->as()->xx(); case TendonBase::yxPropertyKey: - return object->as()->yx(); case TendonBase::xyPropertyKey: - return object->as()->xy(); case TendonBase::yyPropertyKey: - return object->as()->yy(); case TendonBase::txPropertyKey: - return object->as()->tx(); case TendonBase::tyPropertyKey: - return object->as()->ty(); case TextModifierRangeBase::modifyFromPropertyKey: - return object->as()->modifyFrom(); case TextModifierRangeBase::modifyToPropertyKey: - return object->as()->modifyTo(); case TextModifierRangeBase::strengthPropertyKey: - return object->as()->strength(); case TextModifierRangeBase::falloffFromPropertyKey: - return object->as()->falloffFrom(); case TextModifierRangeBase::falloffToPropertyKey: - return object->as()->falloffTo(); case TextModifierRangeBase::offsetPropertyKey: - return object->as()->offset(); case TextVariationModifierBase::axisValuePropertyKey: - return object->as()->axisValue(); case TextModifierGroupBase::originXPropertyKey: - return object->as()->originX(); case TextModifierGroupBase::originYPropertyKey: - return object->as()->originY(); case TextModifierGroupBase::opacityPropertyKey: - return object->as()->opacity(); case TextModifierGroupBase::xPropertyKey: - return object->as()->x(); case TextModifierGroupBase::yPropertyKey: - return object->as()->y(); case TextModifierGroupBase::rotationPropertyKey: - return object->as()->rotation(); case TextModifierGroupBase::scaleXPropertyKey: - return object->as()->scaleX(); case TextModifierGroupBase::scaleYPropertyKey: - return object->as()->scaleY(); case TextStyleBase::fontSizePropertyKey: - return object->as()->fontSize(); case TextStyleBase::lineHeightPropertyKey: - return object->as()->lineHeight(); case TextStyleBase::letterSpacingPropertyKey: - return object->as()->letterSpacing(); case TextStyleAxisBase::axisValuePropertyKey: - return object->as()->axisValue(); case TextBase::widthPropertyKey: - return object->as()->width(); case TextBase::heightPropertyKey: - return object->as()->height(); case TextBase::originXPropertyKey: - return object->as()->originX(); case TextBase::originYPropertyKey: - return object->as()->originY(); case TextBase::paragraphSpacingPropertyKey: - return object->as()->paragraphSpacing(); case DrawableAssetBase::heightPropertyKey: - return object->as()->height(); case DrawableAssetBase::widthPropertyKey: - return object->as()->width(); + case ExportAudioBase::volumePropertyKey: + return CoreDoubleType::id; + case NestedArtboardBase::dataBindPathIdsPropertyKey: + case MeshBase::triangleIndexBytesPropertyKey: + case DataBindContextBase::sourcePathIdsPropertyKey: + case FileAssetBase::cdnUuidPropertyKey: + case FileAssetContentsBase::bytesPropertyKey: + return CoreBytesType::id; + default: + return -1; } - return 0.0f; } - static bool getBool(Core* object, int propertyKey) + static bool isCallback(uint32_t propertyKey) + { + switch (propertyKey) + { + case NestedTriggerBase::firePropertyKey: + case EventBase::triggerPropertyKey: + return true; + default: + return false; + } + } + static bool objectSupportsProperty(Core* object, uint32_t propertyKey) { switch (propertyKey) { + case ViewModelInstanceListItemBase::useLinkedArtboardPropertyKey: + return object->is(); + case ViewModelInstanceBooleanBase::propertyValuePropertyKey: + return object->is(); case TransformComponentConstraintBase::offsetPropertyKey: - return object->as()->offset(); + return object->is(); case TransformComponentConstraintBase::doesCopyPropertyKey: - return object->as()->doesCopy(); + return object->is(); case TransformComponentConstraintBase::minPropertyKey: - return object->as()->min(); + return object->is(); case TransformComponentConstraintBase::maxPropertyKey: - return object->as()->max(); + return object->is(); case TransformComponentConstraintYBase::doesCopyYPropertyKey: - return object->as()->doesCopyY(); + return object->is(); case TransformComponentConstraintYBase::minYPropertyKey: - return object->as()->minY(); + return object->is(); case TransformComponentConstraintYBase::maxYPropertyKey: - return object->as()->maxY(); + return object->is(); case IKConstraintBase::invertDirectionPropertyKey: - return object->as()->invertDirection(); + return object->is(); case FollowPathConstraintBase::orientPropertyKey: - return object->as()->orient(); + return object->is(); case FollowPathConstraintBase::offsetPropertyKey: - return object->as()->offset(); + return object->is(); + case AxisBase::normalizedPropertyKey: + return object->is(); + case LayoutComponentStyleBase::intrinsicallySizedValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::linkCornerRadiusPropertyKey: + return object->is(); case NestedSimpleAnimationBase::isPlayingPropertyKey: - return object->as()->isPlaying(); + return object->is(); case KeyFrameBoolBase::valuePropertyKey: - return object->as()->value(); + return object->is(); + case ListenerAlignTargetBase::preserveOffsetPropertyKey: + return object->is(); + case TransitionValueBooleanComparatorBase::valuePropertyKey: + return object->is(); case NestedBoolBase::nestedValuePropertyKey: - return object->as()->nestedValue(); + return object->is(); case LinearAnimationBase::enableWorkAreaPropertyKey: - return object->as()->enableWorkArea(); + return object->is(); case LinearAnimationBase::quantizePropertyKey: - return object->as()->quantize(); + return object->is(); case StateMachineBoolBase::valuePropertyKey: - return object->as()->value(); + return object->is(); case ShapePaintBase::isVisiblePropertyKey: - return object->as()->isVisible(); + return object->is(); case StrokeBase::transformAffectsStrokePropertyKey: - return object->as()->transformAffectsStroke(); + return object->is(); case PointsPathBase::isClosedPropertyKey: - return object->as()->isClosed(); + return object->is(); case RectangleBase::linkCornerRadiusPropertyKey: - return object->as()->linkCornerRadius(); + return object->is(); case ClippingShapeBase::isVisiblePropertyKey: - return object->as()->isVisible(); + return object->is(); case CustomPropertyBooleanBase::propertyValuePropertyKey: - return object->as()->propertyValue(); - case ArtboardBase::clipPropertyKey: - return object->as()->clip(); + return object->is(); + case LayoutComponentBase::clipPropertyKey: + return object->is(); + case BindablePropertyBooleanBase::propertyValuePropertyKey: + return object->is(); case TextModifierRangeBase::clampPropertyKey: - return object->as()->clamp(); - } - return false; - } - static int getColor(Core* object, int propertyKey) - { - switch (propertyKey) - { - case KeyFrameColorBase::valuePropertyKey: - return object->as()->value(); - case SolidColorBase::colorValuePropertyKey: - return object->as()->colorValue(); - case GradientStopBase::colorValuePropertyKey: - return object->as()->colorValue(); - } - return 0; - } - static int propertyFieldId(int propertyKey) - { - switch (propertyKey) - { - case ComponentBase::namePropertyKey: - case AnimationBase::namePropertyKey: - case StateMachineComponentBase::namePropertyKey: - case KeyFrameStringBase::valuePropertyKey: - case OpenUrlEventBase::urlPropertyKey: - case TextValueRunBase::textPropertyKey: - case CustomPropertyStringBase::propertyValuePropertyKey: - case AssetBase::namePropertyKey: - case FileAssetBase::cdnBaseUrlPropertyKey: - return CoreStringType::id; + return object->is(); + case ViewModelInstanceListItemBase::viewModelIdPropertyKey: + return object->is(); + case ViewModelInstanceListItemBase::viewModelInstanceIdPropertyKey: + return object->is(); + case ViewModelInstanceListItemBase::artboardIdPropertyKey: + return object->is(); + case ViewModelInstanceValueBase::viewModelPropertyIdPropertyKey: + return object->is(); + case ViewModelInstanceEnumBase::propertyValuePropertyKey: + return object->is(); + case ViewModelBase::defaultInstanceIdPropertyKey: + return object->is(); + case ViewModelPropertyViewModelBase::viewModelReferenceIdPropertyKey: + return object->is(); case ComponentBase::parentIdPropertyKey: + return object->is(); + case ViewModelInstanceBase::viewModelIdPropertyKey: + return object->is(); + case ViewModelPropertyEnumBase::enumIdPropertyKey: + return object->is(); + case ViewModelInstanceViewModelBase::propertyValuePropertyKey: + return object->is(); case DrawTargetBase::drawableIdPropertyKey: + return object->is(); case DrawTargetBase::placementValuePropertyKey: + return object->is(); case TargetedConstraintBase::targetIdPropertyKey: + return object->is(); case DistanceConstraintBase::modeValuePropertyKey: + return object->is(); case TransformSpaceConstraintBase::sourceSpaceValuePropertyKey: + return object->is(); case TransformSpaceConstraintBase::destSpaceValuePropertyKey: + return object->is(); case TransformComponentConstraintBase::minMaxSpaceValuePropertyKey: + return object->is(); case IKConstraintBase::parentBoneCountPropertyKey: + return object->is(); case DrawableBase::blendModeValuePropertyKey: + return object->is(); case DrawableBase::drawableFlagsPropertyKey: + return object->is(); case NestedArtboardBase::artboardIdPropertyKey: + return object->is(); case NestedAnimationBase::animationIdPropertyKey: + return object->is(); case SoloBase::activeComponentIdPropertyKey: + return object->is(); + case NestedArtboardLayoutBase::instanceWidthUnitsValuePropertyKey: + return object->is(); + case NestedArtboardLayoutBase::instanceHeightUnitsValuePropertyKey: + return object->is(); + case NestedArtboardLayoutBase::instanceWidthScaleTypePropertyKey: + return object->is(); + case NestedArtboardLayoutBase::instanceHeightScaleTypePropertyKey: + return object->is(); + case NSlicerTileModeBase::patchIndexPropertyKey: + return object->is(); + case NSlicerTileModeBase::stylePropertyKey: + return object->is(); + case LayoutComponentStyleBase::layoutWidthScaleTypePropertyKey: + return object->is(); + case LayoutComponentStyleBase::layoutHeightScaleTypePropertyKey: + return object->is(); + case LayoutComponentStyleBase::layoutAlignmentTypePropertyKey: + return object->is(); + case LayoutComponentStyleBase::animationStyleTypePropertyKey: + return object->is(); + case LayoutComponentStyleBase::interpolationTypePropertyKey: + return object->is(); + case LayoutComponentStyleBase::interpolatorIdPropertyKey: + return object->is(); + case LayoutComponentStyleBase::displayValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::positionTypeValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::flexDirectionValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::directionValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::alignContentValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::alignItemsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::alignSelfValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::justifyContentValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::flexWrapValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::overflowValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::widthUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::heightUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::borderLeftUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::borderRightUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::borderTopUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::borderBottomUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::marginLeftUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::marginRightUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::marginTopUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::marginBottomUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::paddingLeftUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::paddingRightUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::paddingTopUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::paddingBottomUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::positionLeftUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::positionRightUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::positionTopUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::positionBottomUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::gapHorizontalUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::gapVerticalUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::minWidthUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::minHeightUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::maxWidthUnitsValuePropertyKey: + return object->is(); + case LayoutComponentStyleBase::maxHeightUnitsValuePropertyKey: + return object->is(); case ListenerFireEventBase::eventIdPropertyKey: + return object->is(); + case LayerStateBase::flagsPropertyKey: + return object->is(); + case KeyFrameBase::framePropertyKey: + return object->is(); + case InterpolatingKeyFrameBase::interpolationTypePropertyKey: + return object->is(); + case InterpolatingKeyFrameBase::interpolatorIdPropertyKey: + return object->is(); + case KeyFrameUintBase::valuePropertyKey: + return object->is(); case ListenerInputChangeBase::inputIdPropertyKey: + return object->is(); case ListenerInputChangeBase::nestedInputIdPropertyKey: + return object->is(); case AnimationStateBase::animationIdPropertyKey: + return object->is(); case NestedInputBase::inputIdPropertyKey: + return object->is(); case KeyedObjectBase::objectIdPropertyKey: + return object->is(); case BlendAnimationBase::animationIdPropertyKey: + return object->is(); case BlendAnimationDirectBase::inputIdPropertyKey: + return object->is(); case BlendAnimationDirectBase::blendSourcePropertyKey: - case TransitionConditionBase::inputIdPropertyKey: + return object->is(); + case TransitionInputConditionBase::inputIdPropertyKey: + return object->is(); case KeyedPropertyBase::propertyKeyPropertyKey: + return object->is(); case StateMachineListenerBase::targetIdPropertyKey: + return object->is(); case StateMachineListenerBase::listenerTypeValuePropertyKey: + return object->is(); case StateMachineListenerBase::eventIdPropertyKey: - case KeyFrameBase::framePropertyKey: - case InterpolatingKeyFrameBase::interpolationTypePropertyKey: - case InterpolatingKeyFrameBase::interpolatorIdPropertyKey: + return object->is(); + case TransitionPropertyArtboardComparatorBase::propertyTypePropertyKey: + return object->is(); case KeyFrameIdBase::valuePropertyKey: + return object->is(); case ListenerBoolChangeBase::valuePropertyKey: + return object->is(); case ListenerAlignTargetBase::targetIdPropertyKey: + return object->is(); case TransitionValueConditionBase::opValuePropertyKey: + return object->is(); + case TransitionViewModelConditionBase::leftComparatorIdPropertyKey: + return object->is(); + case TransitionViewModelConditionBase::rightComparatorIdPropertyKey: + return object->is(); + case TransitionViewModelConditionBase::opValuePropertyKey: + return object->is(); case StateTransitionBase::stateToIdPropertyKey: + return object->is(); case StateTransitionBase::flagsPropertyKey: + return object->is(); case StateTransitionBase::durationPropertyKey: + return object->is(); case StateTransitionBase::exitTimePropertyKey: + return object->is(); case StateTransitionBase::interpolationTypePropertyKey: + return object->is(); case StateTransitionBase::interpolatorIdPropertyKey: + return object->is(); + case StateTransitionBase::randomWeightPropertyKey: + return object->is(); case StateMachineFireEventBase::eventIdPropertyKey: + return object->is(); case StateMachineFireEventBase::occursValuePropertyKey: + return object->is(); case LinearAnimationBase::fpsPropertyKey: + return object->is(); case LinearAnimationBase::durationPropertyKey: + return object->is(); case LinearAnimationBase::loopValuePropertyKey: + return object->is(); case LinearAnimationBase::workStartPropertyKey: + return object->is(); case LinearAnimationBase::workEndPropertyKey: + return object->is(); case ElasticInterpolatorBase::easingValuePropertyKey: + return object->is(); case BlendState1DBase::inputIdPropertyKey: + return object->is(); + case TransitionValueEnumComparatorBase::valuePropertyKey: + return object->is(); case BlendStateTransitionBase::exitBlendAnimationIdPropertyKey: + return object->is(); case StrokeBase::capPropertyKey: + return object->is(); case StrokeBase::joinPropertyKey: + return object->is(); case TrimPathBase::modeValuePropertyKey: + return object->is(); case FillBase::fillRulePropertyKey: + return object->is(); case PathBase::pathFlagsPropertyKey: + return object->is(); case ClippingShapeBase::sourceIdPropertyKey: + return object->is(); case ClippingShapeBase::fillRulePropertyKey: + return object->is(); case PolygonBase::pointsPropertyKey: + return object->is(); case ImageBase::assetIdPropertyKey: + return object->is(); case DrawRulesBase::drawTargetIdPropertyKey: + return object->is(); + case LayoutComponentBase::styleIdPropertyKey: + return object->is(); case ArtboardBase::defaultStateMachineIdPropertyKey: + return object->is(); + case ArtboardBase::viewModelIdPropertyKey: + return object->is(); case JoystickBase::xIdPropertyKey: + return object->is(); case JoystickBase::yIdPropertyKey: + return object->is(); case JoystickBase::joystickFlagsPropertyKey: + return object->is(); case JoystickBase::handleSourceIdPropertyKey: + return object->is(); case OpenUrlEventBase::targetValuePropertyKey: + return object->is(); + case DataBindBase::propertyKeyPropertyKey: + return object->is(); + case DataBindBase::flagsPropertyKey: + return object->is(); + case DataBindBase::converterIdPropertyKey: + return object->is(); + case DataConverterGroupItemBase::converterIdPropertyKey: + return object->is(); + case DataConverterRounderBase::decimalsPropertyKey: + return object->is(); + case DataConverterOperationBase::operationTypePropertyKey: + return object->is(); + case BindablePropertyEnumBase::propertyValuePropertyKey: + return object->is(); + case NestedArtboardLeafBase::fitPropertyKey: + return object->is(); case WeightBase::valuesPropertyKey: + return object->is(); case WeightBase::indicesPropertyKey: + return object->is(); case TendonBase::boneIdPropertyKey: + return object->is(); case CubicWeightBase::inValuesPropertyKey: + return object->is(); case CubicWeightBase::inIndicesPropertyKey: + return object->is(); case CubicWeightBase::outValuesPropertyKey: + return object->is(); case CubicWeightBase::outIndicesPropertyKey: + return object->is(); case TextModifierRangeBase::unitsValuePropertyKey: + return object->is(); case TextModifierRangeBase::typeValuePropertyKey: + return object->is(); case TextModifierRangeBase::modeValuePropertyKey: + return object->is(); case TextModifierRangeBase::runIdPropertyKey: + return object->is(); case TextStyleFeatureBase::tagPropertyKey: + return object->is(); case TextStyleFeatureBase::featureValuePropertyKey: + return object->is(); case TextVariationModifierBase::axisTagPropertyKey: + return object->is(); case TextModifierGroupBase::modifierFlagsPropertyKey: + return object->is(); case TextStyleBase::fontAssetIdPropertyKey: + return object->is(); case TextStyleAxisBase::tagPropertyKey: + return object->is(); case TextBase::alignValuePropertyKey: + return object->is(); case TextBase::sizingValuePropertyKey: + return object->is(); case TextBase::overflowValuePropertyKey: + return object->is(); case TextBase::originValuePropertyKey: + return object->is(); case TextValueRunBase::styleIdPropertyKey: + return object->is(); case FileAssetBase::assetIdPropertyKey: + return object->is(); case AudioEventBase::assetIdPropertyKey: - return CoreUintType::id; + return object->is(); + case ViewModelInstanceColorBase::propertyValuePropertyKey: + return object->is(); + case KeyFrameColorBase::valuePropertyKey: + return object->is(); + case TransitionValueColorComparatorBase::valuePropertyKey: + return object->is(); + case SolidColorBase::colorValuePropertyKey: + return object->is(); + case GradientStopBase::colorValuePropertyKey: + return object->is(); + case BindablePropertyColorBase::propertyValuePropertyKey: + return object->is(); + case ViewModelComponentBase::namePropertyKey: + return object->is(); + case ViewModelInstanceStringBase::propertyValuePropertyKey: + return object->is(); + case ComponentBase::namePropertyKey: + return object->is(); + case DataEnumValueBase::keyPropertyKey: + return object->is(); + case DataEnumValueBase::valuePropertyKey: + return object->is(); + case AnimationBase::namePropertyKey: + return object->is(); + case StateMachineComponentBase::namePropertyKey: + return object->is(); + case KeyFrameStringBase::valuePropertyKey: + return object->is(); + case TransitionValueStringComparatorBase::valuePropertyKey: + return object->is(); + case OpenUrlEventBase::urlPropertyKey: + return object->is(); + case DataConverterBase::namePropertyKey: + return object->is(); + case BindablePropertyStringBase::propertyValuePropertyKey: + return object->is(); + case TextValueRunBase::textPropertyKey: + return object->is(); + case CustomPropertyStringBase::propertyValuePropertyKey: + return object->is(); + case AssetBase::namePropertyKey: + return object->is(); + case FileAssetBase::cdnBaseUrlPropertyKey: + return object->is(); + case ViewModelInstanceNumberBase::propertyValuePropertyKey: + return object->is(); case CustomPropertyNumberBase::propertyValuePropertyKey: + return object->is(); case ConstraintBase::strengthPropertyKey: + return object->is(); case DistanceConstraintBase::distancePropertyKey: + return object->is(); case TransformComponentConstraintBase::copyFactorPropertyKey: + return object->is(); case TransformComponentConstraintBase::minValuePropertyKey: + return object->is(); case TransformComponentConstraintBase::maxValuePropertyKey: + return object->is(); case TransformComponentConstraintYBase::copyFactorYPropertyKey: + return object->is(); case TransformComponentConstraintYBase::minValueYPropertyKey: + return object->is(); case TransformComponentConstraintYBase::maxValueYPropertyKey: + return object->is(); case FollowPathConstraintBase::distancePropertyKey: + return object->is(); case TransformConstraintBase::originXPropertyKey: + return object->is(); case TransformConstraintBase::originYPropertyKey: + return object->is(); case WorldTransformComponentBase::opacityPropertyKey: + return object->is(); case TransformComponentBase::rotationPropertyKey: + return object->is(); case TransformComponentBase::scaleXPropertyKey: + return object->is(); case TransformComponentBase::scaleYPropertyKey: + return object->is(); case NodeBase::xPropertyKey: + case NodeBase::xArtboardPropertyKey: + return object->is(); case NodeBase::yPropertyKey: + case NodeBase::yArtboardPropertyKey: + return object->is(); + case NestedArtboardLayoutBase::instanceWidthPropertyKey: + return object->is(); + case NestedArtboardLayoutBase::instanceHeightPropertyKey: + return object->is(); + case AxisBase::offsetPropertyKey: + return object->is(); + case LayoutComponentStyleBase::gapHorizontalPropertyKey: + return object->is(); + case LayoutComponentStyleBase::gapVerticalPropertyKey: + return object->is(); + case LayoutComponentStyleBase::maxWidthPropertyKey: + return object->is(); + case LayoutComponentStyleBase::maxHeightPropertyKey: + return object->is(); + case LayoutComponentStyleBase::minWidthPropertyKey: + return object->is(); + case LayoutComponentStyleBase::minHeightPropertyKey: + return object->is(); + case LayoutComponentStyleBase::borderLeftPropertyKey: + return object->is(); + case LayoutComponentStyleBase::borderRightPropertyKey: + return object->is(); + case LayoutComponentStyleBase::borderTopPropertyKey: + return object->is(); + case LayoutComponentStyleBase::borderBottomPropertyKey: + return object->is(); + case LayoutComponentStyleBase::marginLeftPropertyKey: + return object->is(); + case LayoutComponentStyleBase::marginRightPropertyKey: + return object->is(); + case LayoutComponentStyleBase::marginTopPropertyKey: + return object->is(); + case LayoutComponentStyleBase::marginBottomPropertyKey: + return object->is(); + case LayoutComponentStyleBase::paddingLeftPropertyKey: + return object->is(); + case LayoutComponentStyleBase::paddingRightPropertyKey: + return object->is(); + case LayoutComponentStyleBase::paddingTopPropertyKey: + return object->is(); + case LayoutComponentStyleBase::paddingBottomPropertyKey: + return object->is(); + case LayoutComponentStyleBase::positionLeftPropertyKey: + return object->is(); + case LayoutComponentStyleBase::positionRightPropertyKey: + return object->is(); + case LayoutComponentStyleBase::positionTopPropertyKey: + return object->is(); + case LayoutComponentStyleBase::positionBottomPropertyKey: + return object->is(); + case LayoutComponentStyleBase::flexPropertyKey: + return object->is(); + case LayoutComponentStyleBase::flexGrowPropertyKey: + return object->is(); + case LayoutComponentStyleBase::flexShrinkPropertyKey: + return object->is(); + case LayoutComponentStyleBase::flexBasisPropertyKey: + return object->is(); + case LayoutComponentStyleBase::aspectRatioPropertyKey: + return object->is(); + case LayoutComponentStyleBase::interpolationTimePropertyKey: + return object->is(); + case LayoutComponentStyleBase::cornerRadiusTLPropertyKey: + return object->is(); + case LayoutComponentStyleBase::cornerRadiusTRPropertyKey: + return object->is(); + case LayoutComponentStyleBase::cornerRadiusBLPropertyKey: + return object->is(); + case LayoutComponentStyleBase::cornerRadiusBRPropertyKey: + return object->is(); case NestedLinearAnimationBase::mixPropertyKey: + return object->is(); case NestedSimpleAnimationBase::speedPropertyKey: + return object->is(); case AdvanceableStateBase::speedPropertyKey: + return object->is(); case BlendAnimationDirectBase::mixValuePropertyKey: + return object->is(); case StateMachineNumberBase::valuePropertyKey: + return object->is(); case CubicInterpolatorBase::x1PropertyKey: + return object->is(); case CubicInterpolatorBase::y1PropertyKey: + return object->is(); case CubicInterpolatorBase::x2PropertyKey: + return object->is(); case CubicInterpolatorBase::y2PropertyKey: + return object->is(); case TransitionNumberConditionBase::valuePropertyKey: + return object->is(); case CubicInterpolatorComponentBase::x1PropertyKey: + return object->is(); case CubicInterpolatorComponentBase::y1PropertyKey: + return object->is(); case CubicInterpolatorComponentBase::x2PropertyKey: + return object->is(); case CubicInterpolatorComponentBase::y2PropertyKey: + return object->is(); case ListenerNumberChangeBase::valuePropertyKey: + return object->is(); case KeyFrameDoubleBase::valuePropertyKey: + return object->is(); case LinearAnimationBase::speedPropertyKey: + return object->is(); + case TransitionValueNumberComparatorBase::valuePropertyKey: + return object->is(); case ElasticInterpolatorBase::amplitudePropertyKey: + return object->is(); case ElasticInterpolatorBase::periodPropertyKey: + return object->is(); case NestedNumberBase::nestedValuePropertyKey: + return object->is(); case NestedRemapAnimationBase::timePropertyKey: + return object->is(); case BlendAnimation1DBase::valuePropertyKey: + return object->is(); case LinearGradientBase::startXPropertyKey: + return object->is(); case LinearGradientBase::startYPropertyKey: + return object->is(); case LinearGradientBase::endXPropertyKey: + return object->is(); case LinearGradientBase::endYPropertyKey: + return object->is(); case LinearGradientBase::opacityPropertyKey: + return object->is(); case StrokeBase::thicknessPropertyKey: + return object->is(); case GradientStopBase::positionPropertyKey: + return object->is(); case TrimPathBase::startPropertyKey: + return object->is(); case TrimPathBase::endPropertyKey: + return object->is(); case TrimPathBase::offsetPropertyKey: + return object->is(); case VertexBase::xPropertyKey: + return object->is(); case VertexBase::yPropertyKey: + return object->is(); case MeshVertexBase::uPropertyKey: + return object->is(); case MeshVertexBase::vPropertyKey: + return object->is(); case StraightVertexBase::radiusPropertyKey: + return object->is(); case CubicAsymmetricVertexBase::rotationPropertyKey: + return object->is(); case CubicAsymmetricVertexBase::inDistancePropertyKey: + return object->is(); case CubicAsymmetricVertexBase::outDistancePropertyKey: + return object->is(); case ParametricPathBase::widthPropertyKey: + return object->is(); case ParametricPathBase::heightPropertyKey: + return object->is(); case ParametricPathBase::originXPropertyKey: + return object->is(); case ParametricPathBase::originYPropertyKey: + return object->is(); case RectangleBase::cornerRadiusTLPropertyKey: + return object->is(); case RectangleBase::cornerRadiusTRPropertyKey: + return object->is(); case RectangleBase::cornerRadiusBLPropertyKey: + return object->is(); case RectangleBase::cornerRadiusBRPropertyKey: + return object->is(); case CubicMirroredVertexBase::rotationPropertyKey: + return object->is(); case CubicMirroredVertexBase::distancePropertyKey: + return object->is(); case PolygonBase::cornerRadiusPropertyKey: + return object->is(); case StarBase::innerRadiusPropertyKey: + return object->is(); case ImageBase::originXPropertyKey: + return object->is(); case ImageBase::originYPropertyKey: + return object->is(); case CubicDetachedVertexBase::inRotationPropertyKey: + return object->is(); case CubicDetachedVertexBase::inDistancePropertyKey: + return object->is(); case CubicDetachedVertexBase::outRotationPropertyKey: + return object->is(); case CubicDetachedVertexBase::outDistancePropertyKey: - case ArtboardBase::widthPropertyKey: - case ArtboardBase::heightPropertyKey: - case ArtboardBase::xPropertyKey: - case ArtboardBase::yPropertyKey: + return object->is(); + case LayoutComponentBase::widthPropertyKey: + return object->is(); + case LayoutComponentBase::heightPropertyKey: + return object->is(); case ArtboardBase::originXPropertyKey: + return object->is(); case ArtboardBase::originYPropertyKey: + return object->is(); case JoystickBase::xPropertyKey: + return object->is(); case JoystickBase::yPropertyKey: + return object->is(); case JoystickBase::posXPropertyKey: + return object->is(); case JoystickBase::posYPropertyKey: + return object->is(); case JoystickBase::originXPropertyKey: + return object->is(); case JoystickBase::originYPropertyKey: + return object->is(); case JoystickBase::widthPropertyKey: + return object->is(); case JoystickBase::heightPropertyKey: + return object->is(); + case DataConverterOperationBase::valuePropertyKey: + return object->is(); + case BindablePropertyNumberBase::propertyValuePropertyKey: + return object->is(); + case NestedArtboardLeafBase::alignmentXPropertyKey: + return object->is(); + case NestedArtboardLeafBase::alignmentYPropertyKey: + return object->is(); case BoneBase::lengthPropertyKey: + return object->is(); case RootBoneBase::xPropertyKey: + return object->is(); case RootBoneBase::yPropertyKey: + return object->is(); case SkinBase::xxPropertyKey: + return object->is(); case SkinBase::yxPropertyKey: + return object->is(); case SkinBase::xyPropertyKey: + return object->is(); case SkinBase::yyPropertyKey: + return object->is(); case SkinBase::txPropertyKey: + return object->is(); case SkinBase::tyPropertyKey: + return object->is(); case TendonBase::xxPropertyKey: + return object->is(); case TendonBase::yxPropertyKey: + return object->is(); case TendonBase::xyPropertyKey: + return object->is(); case TendonBase::yyPropertyKey: + return object->is(); case TendonBase::txPropertyKey: + return object->is(); case TendonBase::tyPropertyKey: + return object->is(); case TextModifierRangeBase::modifyFromPropertyKey: + return object->is(); case TextModifierRangeBase::modifyToPropertyKey: + return object->is(); case TextModifierRangeBase::strengthPropertyKey: + return object->is(); case TextModifierRangeBase::falloffFromPropertyKey: + return object->is(); case TextModifierRangeBase::falloffToPropertyKey: + return object->is(); case TextModifierRangeBase::offsetPropertyKey: + return object->is(); case TextVariationModifierBase::axisValuePropertyKey: + return object->is(); case TextModifierGroupBase::originXPropertyKey: + return object->is(); case TextModifierGroupBase::originYPropertyKey: + return object->is(); case TextModifierGroupBase::opacityPropertyKey: + return object->is(); case TextModifierGroupBase::xPropertyKey: + return object->is(); case TextModifierGroupBase::yPropertyKey: + return object->is(); case TextModifierGroupBase::rotationPropertyKey: + return object->is(); case TextModifierGroupBase::scaleXPropertyKey: + return object->is(); case TextModifierGroupBase::scaleYPropertyKey: + return object->is(); case TextStyleBase::fontSizePropertyKey: + return object->is(); case TextStyleBase::lineHeightPropertyKey: + return object->is(); case TextStyleBase::letterSpacingPropertyKey: + return object->is(); case TextStyleAxisBase::axisValuePropertyKey: + return object->is(); case TextBase::widthPropertyKey: + return object->is(); case TextBase::heightPropertyKey: + return object->is(); case TextBase::originXPropertyKey: + return object->is(); case TextBase::originYPropertyKey: + return object->is(); case TextBase::paragraphSpacingPropertyKey: + return object->is(); case DrawableAssetBase::heightPropertyKey: + return object->is(); case DrawableAssetBase::widthPropertyKey: - return CoreDoubleType::id; - case TransformComponentConstraintBase::offsetPropertyKey: - case TransformComponentConstraintBase::doesCopyPropertyKey: - case TransformComponentConstraintBase::minPropertyKey: - case TransformComponentConstraintBase::maxPropertyKey: - case TransformComponentConstraintYBase::doesCopyYPropertyKey: - case TransformComponentConstraintYBase::minYPropertyKey: - case TransformComponentConstraintYBase::maxYPropertyKey: - case IKConstraintBase::invertDirectionPropertyKey: - case FollowPathConstraintBase::orientPropertyKey: - case FollowPathConstraintBase::offsetPropertyKey: - case NestedSimpleAnimationBase::isPlayingPropertyKey: - case KeyFrameBoolBase::valuePropertyKey: - case NestedBoolBase::nestedValuePropertyKey: - case LinearAnimationBase::enableWorkAreaPropertyKey: - case LinearAnimationBase::quantizePropertyKey: - case StateMachineBoolBase::valuePropertyKey: - case ShapePaintBase::isVisiblePropertyKey: - case StrokeBase::transformAffectsStrokePropertyKey: - case PointsPathBase::isClosedPropertyKey: - case RectangleBase::linkCornerRadiusPropertyKey: - case ClippingShapeBase::isVisiblePropertyKey: - case CustomPropertyBooleanBase::propertyValuePropertyKey: - case ArtboardBase::clipPropertyKey: - case TextModifierRangeBase::clampPropertyKey: - return CoreBoolType::id; - case KeyFrameColorBase::valuePropertyKey: - case SolidColorBase::colorValuePropertyKey: - case GradientStopBase::colorValuePropertyKey: - return CoreColorType::id; - case MeshBase::triangleIndexBytesPropertyKey: - case FileAssetBase::cdnUuidPropertyKey: - case FileAssetContentsBase::bytesPropertyKey: - return CoreBytesType::id; - default: - return -1; - } - } - static bool isCallback(uint32_t propertyKey) - { - switch (propertyKey) - { + return object->is(); + case ExportAudioBase::volumePropertyKey: + return object->is(); case NestedTriggerBase::firePropertyKey: + return object->is(); case EventBase::triggerPropertyKey: - return true; - default: - return false; + return object->is(); } + return false; } }; } // namespace rive diff --git a/include/rive/generated/data_bind/bindable_property_base.hpp b/include/rive/generated/data_bind/bindable_property_base.hpp new file mode 100644 index 00000000..b5d5f144 --- /dev/null +++ b/include/rive/generated/data_bind/bindable_property_base.hpp @@ -0,0 +1,37 @@ +#ifndef _RIVE_BINDABLE_PROPERTY_BASE_HPP_ +#define _RIVE_BINDABLE_PROPERTY_BASE_HPP_ +#include "rive/core.hpp" +namespace rive +{ +class BindablePropertyBase : public Core +{ +protected: + typedef Core Super; + +public: + static const uint16_t typeKey = 9; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case BindablePropertyBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + void copy(const BindablePropertyBase& object) {} + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override { return false; } + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/data_bind/bindable_property_boolean_base.hpp b/include/rive/generated/data_bind/bindable_property_boolean_base.hpp new file mode 100644 index 00000000..366b5512 --- /dev/null +++ b/include/rive/generated/data_bind/bindable_property_boolean_base.hpp @@ -0,0 +1,71 @@ +#ifndef _RIVE_BINDABLE_PROPERTY_BOOLEAN_BASE_HPP_ +#define _RIVE_BINDABLE_PROPERTY_BOOLEAN_BASE_HPP_ +#include "rive/core/field_types/core_bool_type.hpp" +#include "rive/data_bind/bindable_property.hpp" +namespace rive +{ +class BindablePropertyBooleanBase : public BindableProperty +{ +protected: + typedef BindableProperty Super; + +public: + static const uint16_t typeKey = 472; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case BindablePropertyBooleanBase::typeKey: + case BindablePropertyBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t propertyValuePropertyKey = 634; + +private: + bool m_PropertyValue = false; + +public: + inline bool propertyValue() const { return m_PropertyValue; } + void propertyValue(bool value) + { + if (m_PropertyValue == value) + { + return; + } + m_PropertyValue = value; + propertyValueChanged(); + } + + Core* clone() const override; + void copy(const BindablePropertyBooleanBase& object) + { + m_PropertyValue = object.m_PropertyValue; + BindableProperty::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case propertyValuePropertyKey: + m_PropertyValue = CoreBoolType::deserialize(reader); + return true; + } + return BindableProperty::deserialize(propertyKey, reader); + } + +protected: + virtual void propertyValueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/data_bind/bindable_property_color_base.hpp b/include/rive/generated/data_bind/bindable_property_color_base.hpp new file mode 100644 index 00000000..a14617dd --- /dev/null +++ b/include/rive/generated/data_bind/bindable_property_color_base.hpp @@ -0,0 +1,71 @@ +#ifndef _RIVE_BINDABLE_PROPERTY_COLOR_BASE_HPP_ +#define _RIVE_BINDABLE_PROPERTY_COLOR_BASE_HPP_ +#include "rive/core/field_types/core_color_type.hpp" +#include "rive/data_bind/bindable_property.hpp" +namespace rive +{ +class BindablePropertyColorBase : public BindableProperty +{ +protected: + typedef BindableProperty Super; + +public: + static const uint16_t typeKey = 475; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case BindablePropertyColorBase::typeKey: + case BindablePropertyBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t propertyValuePropertyKey = 638; + +private: + int m_PropertyValue = 0xFF1D1D1D; + +public: + inline int propertyValue() const { return m_PropertyValue; } + void propertyValue(int value) + { + if (m_PropertyValue == value) + { + return; + } + m_PropertyValue = value; + propertyValueChanged(); + } + + Core* clone() const override; + void copy(const BindablePropertyColorBase& object) + { + m_PropertyValue = object.m_PropertyValue; + BindableProperty::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case propertyValuePropertyKey: + m_PropertyValue = CoreColorType::deserialize(reader); + return true; + } + return BindableProperty::deserialize(propertyKey, reader); + } + +protected: + virtual void propertyValueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/data_bind/bindable_property_enum_base.hpp b/include/rive/generated/data_bind/bindable_property_enum_base.hpp new file mode 100644 index 00000000..30c6f445 --- /dev/null +++ b/include/rive/generated/data_bind/bindable_property_enum_base.hpp @@ -0,0 +1,71 @@ +#ifndef _RIVE_BINDABLE_PROPERTY_ENUM_BASE_HPP_ +#define _RIVE_BINDABLE_PROPERTY_ENUM_BASE_HPP_ +#include "rive/core/field_types/core_uint_type.hpp" +#include "rive/data_bind/bindable_property.hpp" +namespace rive +{ +class BindablePropertyEnumBase : public BindableProperty +{ +protected: + typedef BindableProperty Super; + +public: + static const uint16_t typeKey = 474; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case BindablePropertyEnumBase::typeKey: + case BindablePropertyBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t propertyValuePropertyKey = 637; + +private: + uint32_t m_PropertyValue = -1; + +public: + inline uint32_t propertyValue() const { return m_PropertyValue; } + void propertyValue(uint32_t value) + { + if (m_PropertyValue == value) + { + return; + } + m_PropertyValue = value; + propertyValueChanged(); + } + + Core* clone() const override; + void copy(const BindablePropertyEnumBase& object) + { + m_PropertyValue = object.m_PropertyValue; + BindableProperty::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case propertyValuePropertyKey: + m_PropertyValue = CoreUintType::deserialize(reader); + return true; + } + return BindableProperty::deserialize(propertyKey, reader); + } + +protected: + virtual void propertyValueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/data_bind/bindable_property_number_base.hpp b/include/rive/generated/data_bind/bindable_property_number_base.hpp new file mode 100644 index 00000000..72596c12 --- /dev/null +++ b/include/rive/generated/data_bind/bindable_property_number_base.hpp @@ -0,0 +1,71 @@ +#ifndef _RIVE_BINDABLE_PROPERTY_NUMBER_BASE_HPP_ +#define _RIVE_BINDABLE_PROPERTY_NUMBER_BASE_HPP_ +#include "rive/core/field_types/core_double_type.hpp" +#include "rive/data_bind/bindable_property.hpp" +namespace rive +{ +class BindablePropertyNumberBase : public BindableProperty +{ +protected: + typedef BindableProperty Super; + +public: + static const uint16_t typeKey = 473; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case BindablePropertyNumberBase::typeKey: + case BindablePropertyBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t propertyValuePropertyKey = 636; + +private: + float m_PropertyValue = 0.0f; + +public: + inline float propertyValue() const { return m_PropertyValue; } + void propertyValue(float value) + { + if (m_PropertyValue == value) + { + return; + } + m_PropertyValue = value; + propertyValueChanged(); + } + + Core* clone() const override; + void copy(const BindablePropertyNumberBase& object) + { + m_PropertyValue = object.m_PropertyValue; + BindableProperty::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case propertyValuePropertyKey: + m_PropertyValue = CoreDoubleType::deserialize(reader); + return true; + } + return BindableProperty::deserialize(propertyKey, reader); + } + +protected: + virtual void propertyValueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/data_bind/bindable_property_string_base.hpp b/include/rive/generated/data_bind/bindable_property_string_base.hpp new file mode 100644 index 00000000..88d51219 --- /dev/null +++ b/include/rive/generated/data_bind/bindable_property_string_base.hpp @@ -0,0 +1,72 @@ +#ifndef _RIVE_BINDABLE_PROPERTY_STRING_BASE_HPP_ +#define _RIVE_BINDABLE_PROPERTY_STRING_BASE_HPP_ +#include +#include "rive/core/field_types/core_string_type.hpp" +#include "rive/data_bind/bindable_property.hpp" +namespace rive +{ +class BindablePropertyStringBase : public BindableProperty +{ +protected: + typedef BindableProperty Super; + +public: + static const uint16_t typeKey = 471; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case BindablePropertyStringBase::typeKey: + case BindablePropertyBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t propertyValuePropertyKey = 635; + +private: + std::string m_PropertyValue = ""; + +public: + inline const std::string& propertyValue() const { return m_PropertyValue; } + void propertyValue(std::string value) + { + if (m_PropertyValue == value) + { + return; + } + m_PropertyValue = value; + propertyValueChanged(); + } + + Core* clone() const override; + void copy(const BindablePropertyStringBase& object) + { + m_PropertyValue = object.m_PropertyValue; + BindableProperty::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case propertyValuePropertyKey: + m_PropertyValue = CoreStringType::deserialize(reader); + return true; + } + return BindableProperty::deserialize(propertyKey, reader); + } + +protected: + virtual void propertyValueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/data_bind/converters/data_converter_base.hpp b/include/rive/generated/data_bind/converters/data_converter_base.hpp new file mode 100644 index 00000000..737b5679 --- /dev/null +++ b/include/rive/generated/data_bind/converters/data_converter_base.hpp @@ -0,0 +1,66 @@ +#ifndef _RIVE_DATA_CONVERTER_BASE_HPP_ +#define _RIVE_DATA_CONVERTER_BASE_HPP_ +#include +#include "rive/core.hpp" +#include "rive/core/field_types/core_string_type.hpp" +namespace rive +{ +class DataConverterBase : public Core +{ +protected: + typedef Core Super; + +public: + static const uint16_t typeKey = 488; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case DataConverterBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t namePropertyKey = 662; + +private: + std::string m_Name = ""; + +public: + inline const std::string& name() const { return m_Name; } + void name(std::string value) + { + if (m_Name == value) + { + return; + } + m_Name = value; + nameChanged(); + } + + void copy(const DataConverterBase& object) { m_Name = object.m_Name; } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case namePropertyKey: + m_Name = CoreStringType::deserialize(reader); + return true; + } + return false; + } + +protected: + virtual void nameChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/data_bind/converters/data_converter_group_base.hpp b/include/rive/generated/data_bind/converters/data_converter_group_base.hpp new file mode 100644 index 00000000..32377ba4 --- /dev/null +++ b/include/rive/generated/data_bind/converters/data_converter_group_base.hpp @@ -0,0 +1,36 @@ +#ifndef _RIVE_DATA_CONVERTER_GROUP_BASE_HPP_ +#define _RIVE_DATA_CONVERTER_GROUP_BASE_HPP_ +#include "rive/data_bind/converters/data_converter.hpp" +namespace rive +{ +class DataConverterGroupBase : public DataConverter +{ +protected: + typedef DataConverter Super; + +public: + static const uint16_t typeKey = 499; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case DataConverterGroupBase::typeKey: + case DataConverterBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/data_bind/converters/data_converter_group_item_base.hpp b/include/rive/generated/data_bind/converters/data_converter_group_item_base.hpp new file mode 100644 index 00000000..2f512813 --- /dev/null +++ b/include/rive/generated/data_bind/converters/data_converter_group_item_base.hpp @@ -0,0 +1,66 @@ +#ifndef _RIVE_DATA_CONVERTER_GROUP_ITEM_BASE_HPP_ +#define _RIVE_DATA_CONVERTER_GROUP_ITEM_BASE_HPP_ +#include "rive/core.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class DataConverterGroupItemBase : public Core +{ +protected: + typedef Core Super; + +public: + static const uint16_t typeKey = 498; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case DataConverterGroupItemBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t converterIdPropertyKey = 679; + +private: + uint32_t m_ConverterId = -1; + +public: + inline uint32_t converterId() const { return m_ConverterId; } + void converterId(uint32_t value) + { + if (m_ConverterId == value) + { + return; + } + m_ConverterId = value; + converterIdChanged(); + } + + Core* clone() const override; + void copy(const DataConverterGroupItemBase& object) { m_ConverterId = object.m_ConverterId; } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case converterIdPropertyKey: + m_ConverterId = CoreUintType::deserialize(reader); + return true; + } + return false; + } + +protected: + virtual void converterIdChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/data_bind/converters/data_converter_operation_base.hpp b/include/rive/generated/data_bind/converters/data_converter_operation_base.hpp new file mode 100644 index 00000000..12913180 --- /dev/null +++ b/include/rive/generated/data_bind/converters/data_converter_operation_base.hpp @@ -0,0 +1,90 @@ +#ifndef _RIVE_DATA_CONVERTER_OPERATION_BASE_HPP_ +#define _RIVE_DATA_CONVERTER_OPERATION_BASE_HPP_ +#include "rive/core/field_types/core_double_type.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +#include "rive/data_bind/converters/data_converter.hpp" +namespace rive +{ +class DataConverterOperationBase : public DataConverter +{ +protected: + typedef DataConverter Super; + +public: + static const uint16_t typeKey = 500; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case DataConverterOperationBase::typeKey: + case DataConverterBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t valuePropertyKey = 681; + static const uint16_t operationTypePropertyKey = 682; + +private: + float m_Value = 1.0f; + uint32_t m_OperationType = 0; + +public: + inline float value() const { return m_Value; } + void value(float value) + { + if (m_Value == value) + { + return; + } + m_Value = value; + valueChanged(); + } + + inline uint32_t operationType() const { return m_OperationType; } + void operationType(uint32_t value) + { + if (m_OperationType == value) + { + return; + } + m_OperationType = value; + operationTypeChanged(); + } + + Core* clone() const override; + void copy(const DataConverterOperationBase& object) + { + m_Value = object.m_Value; + m_OperationType = object.m_OperationType; + DataConverter::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case valuePropertyKey: + m_Value = CoreDoubleType::deserialize(reader); + return true; + case operationTypePropertyKey: + m_OperationType = CoreUintType::deserialize(reader); + return true; + } + return DataConverter::deserialize(propertyKey, reader); + } + +protected: + virtual void valueChanged() {} + virtual void operationTypeChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/data_bind/converters/data_converter_rounder_base.hpp b/include/rive/generated/data_bind/converters/data_converter_rounder_base.hpp new file mode 100644 index 00000000..b1098fbd --- /dev/null +++ b/include/rive/generated/data_bind/converters/data_converter_rounder_base.hpp @@ -0,0 +1,71 @@ +#ifndef _RIVE_DATA_CONVERTER_ROUNDER_BASE_HPP_ +#define _RIVE_DATA_CONVERTER_ROUNDER_BASE_HPP_ +#include "rive/core/field_types/core_uint_type.hpp" +#include "rive/data_bind/converters/data_converter.hpp" +namespace rive +{ +class DataConverterRounderBase : public DataConverter +{ +protected: + typedef DataConverter Super; + +public: + static const uint16_t typeKey = 489; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case DataConverterRounderBase::typeKey: + case DataConverterBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t decimalsPropertyKey = 669; + +private: + uint32_t m_Decimals = 0; + +public: + inline uint32_t decimals() const { return m_Decimals; } + void decimals(uint32_t value) + { + if (m_Decimals == value) + { + return; + } + m_Decimals = value; + decimalsChanged(); + } + + Core* clone() const override; + void copy(const DataConverterRounderBase& object) + { + m_Decimals = object.m_Decimals; + DataConverter::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case decimalsPropertyKey: + m_Decimals = CoreUintType::deserialize(reader); + return true; + } + return DataConverter::deserialize(propertyKey, reader); + } + +protected: + virtual void decimalsChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/data_bind/converters/data_converter_to_string_base.hpp b/include/rive/generated/data_bind/converters/data_converter_to_string_base.hpp new file mode 100644 index 00000000..c919cbad --- /dev/null +++ b/include/rive/generated/data_bind/converters/data_converter_to_string_base.hpp @@ -0,0 +1,36 @@ +#ifndef _RIVE_DATA_CONVERTER_TO_STRING_BASE_HPP_ +#define _RIVE_DATA_CONVERTER_TO_STRING_BASE_HPP_ +#include "rive/data_bind/converters/data_converter.hpp" +namespace rive +{ +class DataConverterToStringBase : public DataConverter +{ +protected: + typedef DataConverter Super; + +public: + static const uint16_t typeKey = 490; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case DataConverterToStringBase::typeKey: + case DataConverterBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/data_bind/data_bind_base.hpp b/include/rive/generated/data_bind/data_bind_base.hpp new file mode 100644 index 00000000..77db0216 --- /dev/null +++ b/include/rive/generated/data_bind/data_bind_base.hpp @@ -0,0 +1,105 @@ +#ifndef _RIVE_DATA_BIND_BASE_HPP_ +#define _RIVE_DATA_BIND_BASE_HPP_ +#include "rive/core.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class DataBindBase : public Core +{ +protected: + typedef Core Super; + +public: + static const uint16_t typeKey = 446; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case DataBindBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t propertyKeyPropertyKey = 586; + static const uint16_t flagsPropertyKey = 587; + static const uint16_t converterIdPropertyKey = 660; + +private: + uint32_t m_PropertyKey = Core::invalidPropertyKey; + uint32_t m_Flags = 0; + uint32_t m_ConverterId = -1; + +public: + inline uint32_t propertyKey() const { return m_PropertyKey; } + void propertyKey(uint32_t value) + { + if (m_PropertyKey == value) + { + return; + } + m_PropertyKey = value; + propertyKeyChanged(); + } + + inline uint32_t flags() const { return m_Flags; } + void flags(uint32_t value) + { + if (m_Flags == value) + { + return; + } + m_Flags = value; + flagsChanged(); + } + + inline uint32_t converterId() const { return m_ConverterId; } + void converterId(uint32_t value) + { + if (m_ConverterId == value) + { + return; + } + m_ConverterId = value; + converterIdChanged(); + } + + Core* clone() const override; + void copy(const DataBindBase& object) + { + m_PropertyKey = object.m_PropertyKey; + m_Flags = object.m_Flags; + m_ConverterId = object.m_ConverterId; + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case propertyKeyPropertyKey: + m_PropertyKey = CoreUintType::deserialize(reader); + return true; + case flagsPropertyKey: + m_Flags = CoreUintType::deserialize(reader); + return true; + case converterIdPropertyKey: + m_ConverterId = CoreUintType::deserialize(reader); + return true; + } + return false; + } + +protected: + virtual void propertyKeyChanged() {} + virtual void flagsChanged() {} + virtual void converterIdChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/data_bind/data_bind_context_base.hpp b/include/rive/generated/data_bind/data_bind_context_base.hpp new file mode 100644 index 00000000..e8b772ac --- /dev/null +++ b/include/rive/generated/data_bind/data_bind_context_base.hpp @@ -0,0 +1,61 @@ +#ifndef _RIVE_DATA_BIND_CONTEXT_BASE_HPP_ +#define _RIVE_DATA_BIND_CONTEXT_BASE_HPP_ +#include "rive/core/field_types/core_bytes_type.hpp" +#include "rive/data_bind/data_bind.hpp" +#include "rive/span.hpp" +namespace rive +{ +class DataBindContextBase : public DataBind +{ +protected: + typedef DataBind Super; + +public: + static const uint16_t typeKey = 447; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case DataBindContextBase::typeKey: + case DataBindBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t sourcePathIdsPropertyKey = 588; + +public: + virtual void decodeSourcePathIds(Span value) = 0; + virtual void copySourcePathIds(const DataBindContextBase& object) = 0; + + Core* clone() const override; + void copy(const DataBindContextBase& object) + { + copySourcePathIds(object); + DataBind::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case sourcePathIdsPropertyKey: + decodeSourcePathIds(CoreBytesType::deserialize(reader)); + return true; + } + return DataBind::deserialize(propertyKey, reader); + } + +protected: + virtual void sourcePathIdsChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/layout/axis_base.hpp b/include/rive/generated/layout/axis_base.hpp new file mode 100644 index 00000000..b7bf729a --- /dev/null +++ b/include/rive/generated/layout/axis_base.hpp @@ -0,0 +1,89 @@ +#ifndef _RIVE_AXIS_BASE_HPP_ +#define _RIVE_AXIS_BASE_HPP_ +#include "rive/component.hpp" +#include "rive/core/field_types/core_bool_type.hpp" +#include "rive/core/field_types/core_double_type.hpp" +namespace rive +{ +class AxisBase : public Component +{ +protected: + typedef Component Super; + +public: + static const uint16_t typeKey = 492; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case AxisBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t offsetPropertyKey = 675; + static const uint16_t normalizedPropertyKey = 676; + +private: + float m_Offset = 0.0f; + bool m_Normalized = false; + +public: + inline float offset() const { return m_Offset; } + void offset(float value) + { + if (m_Offset == value) + { + return; + } + m_Offset = value; + offsetChanged(); + } + + inline bool normalized() const { return m_Normalized; } + void normalized(bool value) + { + if (m_Normalized == value) + { + return; + } + m_Normalized = value; + normalizedChanged(); + } + + void copy(const AxisBase& object) + { + m_Offset = object.m_Offset; + m_Normalized = object.m_Normalized; + Component::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case offsetPropertyKey: + m_Offset = CoreDoubleType::deserialize(reader); + return true; + case normalizedPropertyKey: + m_Normalized = CoreBoolType::deserialize(reader); + return true; + } + return Component::deserialize(propertyKey, reader); + } + +protected: + virtual void offsetChanged() {} + virtual void normalizedChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/layout/axis_x_base.hpp b/include/rive/generated/layout/axis_x_base.hpp new file mode 100644 index 00000000..07859620 --- /dev/null +++ b/include/rive/generated/layout/axis_x_base.hpp @@ -0,0 +1,37 @@ +#ifndef _RIVE_AXIS_XBASE_HPP_ +#define _RIVE_AXIS_XBASE_HPP_ +#include "rive/layout/axis.hpp" +namespace rive +{ +class AxisXBase : public Axis +{ +protected: + typedef Axis Super; + +public: + static const uint16_t typeKey = 495; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case AxisXBase::typeKey: + case AxisBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/layout/axis_y_base.hpp b/include/rive/generated/layout/axis_y_base.hpp new file mode 100644 index 00000000..e54b22a3 --- /dev/null +++ b/include/rive/generated/layout/axis_y_base.hpp @@ -0,0 +1,37 @@ +#ifndef _RIVE_AXIS_YBASE_HPP_ +#define _RIVE_AXIS_YBASE_HPP_ +#include "rive/layout/axis.hpp" +namespace rive +{ +class AxisYBase : public Axis +{ +protected: + typedef Axis Super; + +public: + static const uint16_t typeKey = 494; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case AxisYBase::typeKey: + case AxisBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/layout/layout_component_style_base.hpp b/include/rive/generated/layout/layout_component_style_base.hpp new file mode 100644 index 00000000..13003f5b --- /dev/null +++ b/include/rive/generated/layout/layout_component_style_base.hpp @@ -0,0 +1,1387 @@ +#ifndef _RIVE_LAYOUT_COMPONENT_STYLE_BASE_HPP_ +#define _RIVE_LAYOUT_COMPONENT_STYLE_BASE_HPP_ +#include "rive/component.hpp" +#include "rive/core/field_types/core_bool_type.hpp" +#include "rive/core/field_types/core_double_type.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class LayoutComponentStyleBase : public Component +{ +protected: + typedef Component Super; + +public: + static const uint16_t typeKey = 420; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case LayoutComponentStyleBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t gapHorizontalPropertyKey = 498; + static const uint16_t gapVerticalPropertyKey = 499; + static const uint16_t maxWidthPropertyKey = 500; + static const uint16_t maxHeightPropertyKey = 501; + static const uint16_t minWidthPropertyKey = 502; + static const uint16_t minHeightPropertyKey = 503; + static const uint16_t borderLeftPropertyKey = 504; + static const uint16_t borderRightPropertyKey = 505; + static const uint16_t borderTopPropertyKey = 506; + static const uint16_t borderBottomPropertyKey = 507; + static const uint16_t marginLeftPropertyKey = 508; + static const uint16_t marginRightPropertyKey = 509; + static const uint16_t marginTopPropertyKey = 510; + static const uint16_t marginBottomPropertyKey = 511; + static const uint16_t paddingLeftPropertyKey = 512; + static const uint16_t paddingRightPropertyKey = 513; + static const uint16_t paddingTopPropertyKey = 514; + static const uint16_t paddingBottomPropertyKey = 515; + static const uint16_t positionLeftPropertyKey = 516; + static const uint16_t positionRightPropertyKey = 517; + static const uint16_t positionTopPropertyKey = 518; + static const uint16_t positionBottomPropertyKey = 519; + static const uint16_t flexPropertyKey = 520; + static const uint16_t flexGrowPropertyKey = 521; + static const uint16_t flexShrinkPropertyKey = 522; + static const uint16_t flexBasisPropertyKey = 523; + static const uint16_t aspectRatioPropertyKey = 524; + static const uint16_t layoutWidthScaleTypePropertyKey = 655; + static const uint16_t layoutHeightScaleTypePropertyKey = 656; + static const uint16_t layoutAlignmentTypePropertyKey = 632; + static const uint16_t animationStyleTypePropertyKey = 589; + static const uint16_t interpolationTypePropertyKey = 590; + static const uint16_t interpolatorIdPropertyKey = 591; + static const uint16_t interpolationTimePropertyKey = 592; + static const uint16_t displayValuePropertyKey = 596; + static const uint16_t positionTypeValuePropertyKey = 597; + static const uint16_t flexDirectionValuePropertyKey = 598; + static const uint16_t directionValuePropertyKey = 599; + static const uint16_t alignContentValuePropertyKey = 600; + static const uint16_t alignItemsValuePropertyKey = 601; + static const uint16_t alignSelfValuePropertyKey = 602; + static const uint16_t justifyContentValuePropertyKey = 603; + static const uint16_t flexWrapValuePropertyKey = 604; + static const uint16_t overflowValuePropertyKey = 605; + static const uint16_t intrinsicallySizedValuePropertyKey = 606; + static const uint16_t widthUnitsValuePropertyKey = 607; + static const uint16_t heightUnitsValuePropertyKey = 608; + static const uint16_t borderLeftUnitsValuePropertyKey = 609; + static const uint16_t borderRightUnitsValuePropertyKey = 610; + static const uint16_t borderTopUnitsValuePropertyKey = 611; + static const uint16_t borderBottomUnitsValuePropertyKey = 612; + static const uint16_t marginLeftUnitsValuePropertyKey = 613; + static const uint16_t marginRightUnitsValuePropertyKey = 614; + static const uint16_t marginTopUnitsValuePropertyKey = 615; + static const uint16_t marginBottomUnitsValuePropertyKey = 616; + static const uint16_t paddingLeftUnitsValuePropertyKey = 617; + static const uint16_t paddingRightUnitsValuePropertyKey = 618; + static const uint16_t paddingTopUnitsValuePropertyKey = 619; + static const uint16_t paddingBottomUnitsValuePropertyKey = 620; + static const uint16_t positionLeftUnitsValuePropertyKey = 621; + static const uint16_t positionRightUnitsValuePropertyKey = 622; + static const uint16_t positionTopUnitsValuePropertyKey = 623; + static const uint16_t positionBottomUnitsValuePropertyKey = 624; + static const uint16_t gapHorizontalUnitsValuePropertyKey = 625; + static const uint16_t gapVerticalUnitsValuePropertyKey = 626; + static const uint16_t minWidthUnitsValuePropertyKey = 627; + static const uint16_t minHeightUnitsValuePropertyKey = 628; + static const uint16_t maxWidthUnitsValuePropertyKey = 629; + static const uint16_t maxHeightUnitsValuePropertyKey = 630; + static const uint16_t linkCornerRadiusPropertyKey = 639; + static const uint16_t cornerRadiusTLPropertyKey = 640; + static const uint16_t cornerRadiusTRPropertyKey = 641; + static const uint16_t cornerRadiusBLPropertyKey = 642; + static const uint16_t cornerRadiusBRPropertyKey = 643; + +private: + float m_GapHorizontal = 0.0f; + float m_GapVertical = 0.0f; + float m_MaxWidth = 0.0f; + float m_MaxHeight = 0.0f; + float m_MinWidth = 0.0f; + float m_MinHeight = 0.0f; + float m_BorderLeft = 0.0f; + float m_BorderRight = 0.0f; + float m_BorderTop = 0.0f; + float m_BorderBottom = 0.0f; + float m_MarginLeft = 0.0f; + float m_MarginRight = 0.0f; + float m_MarginTop = 0.0f; + float m_MarginBottom = 0.0f; + float m_PaddingLeft = 0.0f; + float m_PaddingRight = 0.0f; + float m_PaddingTop = 0.0f; + float m_PaddingBottom = 0.0f; + float m_PositionLeft = 0.0f; + float m_PositionRight = 0.0f; + float m_PositionTop = 0.0f; + float m_PositionBottom = 0.0f; + float m_Flex = 0.0f; + float m_FlexGrow = 0.0f; + float m_FlexShrink = 1.0f; + float m_FlexBasis = 1.0f; + float m_AspectRatio = 0.0f; + uint32_t m_LayoutWidthScaleType = 0; + uint32_t m_LayoutHeightScaleType = 0; + uint32_t m_LayoutAlignmentType = 0; + uint32_t m_AnimationStyleType = 0; + uint32_t m_InterpolationType = 0; + uint32_t m_InterpolatorId = -1; + float m_InterpolationTime = 0.0f; + uint32_t m_DisplayValue = 0; + uint32_t m_PositionTypeValue = 1; + uint32_t m_FlexDirectionValue = 2; + uint32_t m_DirectionValue = 0; + uint32_t m_AlignContentValue = 0; + uint32_t m_AlignItemsValue = 1; + uint32_t m_AlignSelfValue = 0; + uint32_t m_JustifyContentValue = 0; + uint32_t m_FlexWrapValue = 0; + uint32_t m_OverflowValue = 0; + bool m_IntrinsicallySizedValue = false; + uint32_t m_WidthUnitsValue = 1; + uint32_t m_HeightUnitsValue = 1; + uint32_t m_BorderLeftUnitsValue = 0; + uint32_t m_BorderRightUnitsValue = 0; + uint32_t m_BorderTopUnitsValue = 0; + uint32_t m_BorderBottomUnitsValue = 0; + uint32_t m_MarginLeftUnitsValue = 0; + uint32_t m_MarginRightUnitsValue = 0; + uint32_t m_MarginTopUnitsValue = 0; + uint32_t m_MarginBottomUnitsValue = 0; + uint32_t m_PaddingLeftUnitsValue = 0; + uint32_t m_PaddingRightUnitsValue = 0; + uint32_t m_PaddingTopUnitsValue = 0; + uint32_t m_PaddingBottomUnitsValue = 0; + uint32_t m_PositionLeftUnitsValue = 0; + uint32_t m_PositionRightUnitsValue = 0; + uint32_t m_PositionTopUnitsValue = 0; + uint32_t m_PositionBottomUnitsValue = 0; + uint32_t m_GapHorizontalUnitsValue = 0; + uint32_t m_GapVerticalUnitsValue = 0; + uint32_t m_MinWidthUnitsValue = 0; + uint32_t m_MinHeightUnitsValue = 0; + uint32_t m_MaxWidthUnitsValue = 0; + uint32_t m_MaxHeightUnitsValue = 0; + bool m_LinkCornerRadius = true; + float m_CornerRadiusTL = 0.0f; + float m_CornerRadiusTR = 0.0f; + float m_CornerRadiusBL = 0.0f; + float m_CornerRadiusBR = 0.0f; + +public: + inline float gapHorizontal() const { return m_GapHorizontal; } + void gapHorizontal(float value) + { + if (m_GapHorizontal == value) + { + return; + } + m_GapHorizontal = value; + gapHorizontalChanged(); + } + + inline float gapVertical() const { return m_GapVertical; } + void gapVertical(float value) + { + if (m_GapVertical == value) + { + return; + } + m_GapVertical = value; + gapVerticalChanged(); + } + + inline float maxWidth() const { return m_MaxWidth; } + void maxWidth(float value) + { + if (m_MaxWidth == value) + { + return; + } + m_MaxWidth = value; + maxWidthChanged(); + } + + inline float maxHeight() const { return m_MaxHeight; } + void maxHeight(float value) + { + if (m_MaxHeight == value) + { + return; + } + m_MaxHeight = value; + maxHeightChanged(); + } + + inline float minWidth() const { return m_MinWidth; } + void minWidth(float value) + { + if (m_MinWidth == value) + { + return; + } + m_MinWidth = value; + minWidthChanged(); + } + + inline float minHeight() const { return m_MinHeight; } + void minHeight(float value) + { + if (m_MinHeight == value) + { + return; + } + m_MinHeight = value; + minHeightChanged(); + } + + inline float borderLeft() const { return m_BorderLeft; } + void borderLeft(float value) + { + if (m_BorderLeft == value) + { + return; + } + m_BorderLeft = value; + borderLeftChanged(); + } + + inline float borderRight() const { return m_BorderRight; } + void borderRight(float value) + { + if (m_BorderRight == value) + { + return; + } + m_BorderRight = value; + borderRightChanged(); + } + + inline float borderTop() const { return m_BorderTop; } + void borderTop(float value) + { + if (m_BorderTop == value) + { + return; + } + m_BorderTop = value; + borderTopChanged(); + } + + inline float borderBottom() const { return m_BorderBottom; } + void borderBottom(float value) + { + if (m_BorderBottom == value) + { + return; + } + m_BorderBottom = value; + borderBottomChanged(); + } + + inline float marginLeft() const { return m_MarginLeft; } + void marginLeft(float value) + { + if (m_MarginLeft == value) + { + return; + } + m_MarginLeft = value; + marginLeftChanged(); + } + + inline float marginRight() const { return m_MarginRight; } + void marginRight(float value) + { + if (m_MarginRight == value) + { + return; + } + m_MarginRight = value; + marginRightChanged(); + } + + inline float marginTop() const { return m_MarginTop; } + void marginTop(float value) + { + if (m_MarginTop == value) + { + return; + } + m_MarginTop = value; + marginTopChanged(); + } + + inline float marginBottom() const { return m_MarginBottom; } + void marginBottom(float value) + { + if (m_MarginBottom == value) + { + return; + } + m_MarginBottom = value; + marginBottomChanged(); + } + + inline float paddingLeft() const { return m_PaddingLeft; } + void paddingLeft(float value) + { + if (m_PaddingLeft == value) + { + return; + } + m_PaddingLeft = value; + paddingLeftChanged(); + } + + inline float paddingRight() const { return m_PaddingRight; } + void paddingRight(float value) + { + if (m_PaddingRight == value) + { + return; + } + m_PaddingRight = value; + paddingRightChanged(); + } + + inline float paddingTop() const { return m_PaddingTop; } + void paddingTop(float value) + { + if (m_PaddingTop == value) + { + return; + } + m_PaddingTop = value; + paddingTopChanged(); + } + + inline float paddingBottom() const { return m_PaddingBottom; } + void paddingBottom(float value) + { + if (m_PaddingBottom == value) + { + return; + } + m_PaddingBottom = value; + paddingBottomChanged(); + } + + inline float positionLeft() const { return m_PositionLeft; } + void positionLeft(float value) + { + if (m_PositionLeft == value) + { + return; + } + m_PositionLeft = value; + positionLeftChanged(); + } + + inline float positionRight() const { return m_PositionRight; } + void positionRight(float value) + { + if (m_PositionRight == value) + { + return; + } + m_PositionRight = value; + positionRightChanged(); + } + + inline float positionTop() const { return m_PositionTop; } + void positionTop(float value) + { + if (m_PositionTop == value) + { + return; + } + m_PositionTop = value; + positionTopChanged(); + } + + inline float positionBottom() const { return m_PositionBottom; } + void positionBottom(float value) + { + if (m_PositionBottom == value) + { + return; + } + m_PositionBottom = value; + positionBottomChanged(); + } + + inline float flex() const { return m_Flex; } + void flex(float value) + { + if (m_Flex == value) + { + return; + } + m_Flex = value; + flexChanged(); + } + + inline float flexGrow() const { return m_FlexGrow; } + void flexGrow(float value) + { + if (m_FlexGrow == value) + { + return; + } + m_FlexGrow = value; + flexGrowChanged(); + } + + inline float flexShrink() const { return m_FlexShrink; } + void flexShrink(float value) + { + if (m_FlexShrink == value) + { + return; + } + m_FlexShrink = value; + flexShrinkChanged(); + } + + inline float flexBasis() const { return m_FlexBasis; } + void flexBasis(float value) + { + if (m_FlexBasis == value) + { + return; + } + m_FlexBasis = value; + flexBasisChanged(); + } + + inline float aspectRatio() const { return m_AspectRatio; } + void aspectRatio(float value) + { + if (m_AspectRatio == value) + { + return; + } + m_AspectRatio = value; + aspectRatioChanged(); + } + + inline uint32_t layoutWidthScaleType() const { return m_LayoutWidthScaleType; } + void layoutWidthScaleType(uint32_t value) + { + if (m_LayoutWidthScaleType == value) + { + return; + } + m_LayoutWidthScaleType = value; + layoutWidthScaleTypeChanged(); + } + + inline uint32_t layoutHeightScaleType() const { return m_LayoutHeightScaleType; } + void layoutHeightScaleType(uint32_t value) + { + if (m_LayoutHeightScaleType == value) + { + return; + } + m_LayoutHeightScaleType = value; + layoutHeightScaleTypeChanged(); + } + + inline uint32_t layoutAlignmentType() const { return m_LayoutAlignmentType; } + void layoutAlignmentType(uint32_t value) + { + if (m_LayoutAlignmentType == value) + { + return; + } + m_LayoutAlignmentType = value; + layoutAlignmentTypeChanged(); + } + + inline uint32_t animationStyleType() const { return m_AnimationStyleType; } + void animationStyleType(uint32_t value) + { + if (m_AnimationStyleType == value) + { + return; + } + m_AnimationStyleType = value; + animationStyleTypeChanged(); + } + + inline uint32_t interpolationType() const { return m_InterpolationType; } + void interpolationType(uint32_t value) + { + if (m_InterpolationType == value) + { + return; + } + m_InterpolationType = value; + interpolationTypeChanged(); + } + + inline uint32_t interpolatorId() const { return m_InterpolatorId; } + void interpolatorId(uint32_t value) + { + if (m_InterpolatorId == value) + { + return; + } + m_InterpolatorId = value; + interpolatorIdChanged(); + } + + inline float interpolationTime() const { return m_InterpolationTime; } + void interpolationTime(float value) + { + if (m_InterpolationTime == value) + { + return; + } + m_InterpolationTime = value; + interpolationTimeChanged(); + } + + inline uint32_t displayValue() const { return m_DisplayValue; } + void displayValue(uint32_t value) + { + if (m_DisplayValue == value) + { + return; + } + m_DisplayValue = value; + displayValueChanged(); + } + + inline uint32_t positionTypeValue() const { return m_PositionTypeValue; } + void positionTypeValue(uint32_t value) + { + if (m_PositionTypeValue == value) + { + return; + } + m_PositionTypeValue = value; + positionTypeValueChanged(); + } + + inline uint32_t flexDirectionValue() const { return m_FlexDirectionValue; } + void flexDirectionValue(uint32_t value) + { + if (m_FlexDirectionValue == value) + { + return; + } + m_FlexDirectionValue = value; + flexDirectionValueChanged(); + } + + inline uint32_t directionValue() const { return m_DirectionValue; } + void directionValue(uint32_t value) + { + if (m_DirectionValue == value) + { + return; + } + m_DirectionValue = value; + directionValueChanged(); + } + + inline uint32_t alignContentValue() const { return m_AlignContentValue; } + void alignContentValue(uint32_t value) + { + if (m_AlignContentValue == value) + { + return; + } + m_AlignContentValue = value; + alignContentValueChanged(); + } + + inline uint32_t alignItemsValue() const { return m_AlignItemsValue; } + void alignItemsValue(uint32_t value) + { + if (m_AlignItemsValue == value) + { + return; + } + m_AlignItemsValue = value; + alignItemsValueChanged(); + } + + inline uint32_t alignSelfValue() const { return m_AlignSelfValue; } + void alignSelfValue(uint32_t value) + { + if (m_AlignSelfValue == value) + { + return; + } + m_AlignSelfValue = value; + alignSelfValueChanged(); + } + + inline uint32_t justifyContentValue() const { return m_JustifyContentValue; } + void justifyContentValue(uint32_t value) + { + if (m_JustifyContentValue == value) + { + return; + } + m_JustifyContentValue = value; + justifyContentValueChanged(); + } + + inline uint32_t flexWrapValue() const { return m_FlexWrapValue; } + void flexWrapValue(uint32_t value) + { + if (m_FlexWrapValue == value) + { + return; + } + m_FlexWrapValue = value; + flexWrapValueChanged(); + } + + inline uint32_t overflowValue() const { return m_OverflowValue; } + void overflowValue(uint32_t value) + { + if (m_OverflowValue == value) + { + return; + } + m_OverflowValue = value; + overflowValueChanged(); + } + + inline bool intrinsicallySizedValue() const { return m_IntrinsicallySizedValue; } + void intrinsicallySizedValue(bool value) + { + if (m_IntrinsicallySizedValue == value) + { + return; + } + m_IntrinsicallySizedValue = value; + intrinsicallySizedValueChanged(); + } + + inline uint32_t widthUnitsValue() const { return m_WidthUnitsValue; } + void widthUnitsValue(uint32_t value) + { + if (m_WidthUnitsValue == value) + { + return; + } + m_WidthUnitsValue = value; + widthUnitsValueChanged(); + } + + inline uint32_t heightUnitsValue() const { return m_HeightUnitsValue; } + void heightUnitsValue(uint32_t value) + { + if (m_HeightUnitsValue == value) + { + return; + } + m_HeightUnitsValue = value; + heightUnitsValueChanged(); + } + + inline uint32_t borderLeftUnitsValue() const { return m_BorderLeftUnitsValue; } + void borderLeftUnitsValue(uint32_t value) + { + if (m_BorderLeftUnitsValue == value) + { + return; + } + m_BorderLeftUnitsValue = value; + borderLeftUnitsValueChanged(); + } + + inline uint32_t borderRightUnitsValue() const { return m_BorderRightUnitsValue; } + void borderRightUnitsValue(uint32_t value) + { + if (m_BorderRightUnitsValue == value) + { + return; + } + m_BorderRightUnitsValue = value; + borderRightUnitsValueChanged(); + } + + inline uint32_t borderTopUnitsValue() const { return m_BorderTopUnitsValue; } + void borderTopUnitsValue(uint32_t value) + { + if (m_BorderTopUnitsValue == value) + { + return; + } + m_BorderTopUnitsValue = value; + borderTopUnitsValueChanged(); + } + + inline uint32_t borderBottomUnitsValue() const { return m_BorderBottomUnitsValue; } + void borderBottomUnitsValue(uint32_t value) + { + if (m_BorderBottomUnitsValue == value) + { + return; + } + m_BorderBottomUnitsValue = value; + borderBottomUnitsValueChanged(); + } + + inline uint32_t marginLeftUnitsValue() const { return m_MarginLeftUnitsValue; } + void marginLeftUnitsValue(uint32_t value) + { + if (m_MarginLeftUnitsValue == value) + { + return; + } + m_MarginLeftUnitsValue = value; + marginLeftUnitsValueChanged(); + } + + inline uint32_t marginRightUnitsValue() const { return m_MarginRightUnitsValue; } + void marginRightUnitsValue(uint32_t value) + { + if (m_MarginRightUnitsValue == value) + { + return; + } + m_MarginRightUnitsValue = value; + marginRightUnitsValueChanged(); + } + + inline uint32_t marginTopUnitsValue() const { return m_MarginTopUnitsValue; } + void marginTopUnitsValue(uint32_t value) + { + if (m_MarginTopUnitsValue == value) + { + return; + } + m_MarginTopUnitsValue = value; + marginTopUnitsValueChanged(); + } + + inline uint32_t marginBottomUnitsValue() const { return m_MarginBottomUnitsValue; } + void marginBottomUnitsValue(uint32_t value) + { + if (m_MarginBottomUnitsValue == value) + { + return; + } + m_MarginBottomUnitsValue = value; + marginBottomUnitsValueChanged(); + } + + inline uint32_t paddingLeftUnitsValue() const { return m_PaddingLeftUnitsValue; } + void paddingLeftUnitsValue(uint32_t value) + { + if (m_PaddingLeftUnitsValue == value) + { + return; + } + m_PaddingLeftUnitsValue = value; + paddingLeftUnitsValueChanged(); + } + + inline uint32_t paddingRightUnitsValue() const { return m_PaddingRightUnitsValue; } + void paddingRightUnitsValue(uint32_t value) + { + if (m_PaddingRightUnitsValue == value) + { + return; + } + m_PaddingRightUnitsValue = value; + paddingRightUnitsValueChanged(); + } + + inline uint32_t paddingTopUnitsValue() const { return m_PaddingTopUnitsValue; } + void paddingTopUnitsValue(uint32_t value) + { + if (m_PaddingTopUnitsValue == value) + { + return; + } + m_PaddingTopUnitsValue = value; + paddingTopUnitsValueChanged(); + } + + inline uint32_t paddingBottomUnitsValue() const { return m_PaddingBottomUnitsValue; } + void paddingBottomUnitsValue(uint32_t value) + { + if (m_PaddingBottomUnitsValue == value) + { + return; + } + m_PaddingBottomUnitsValue = value; + paddingBottomUnitsValueChanged(); + } + + inline uint32_t positionLeftUnitsValue() const { return m_PositionLeftUnitsValue; } + void positionLeftUnitsValue(uint32_t value) + { + if (m_PositionLeftUnitsValue == value) + { + return; + } + m_PositionLeftUnitsValue = value; + positionLeftUnitsValueChanged(); + } + + inline uint32_t positionRightUnitsValue() const { return m_PositionRightUnitsValue; } + void positionRightUnitsValue(uint32_t value) + { + if (m_PositionRightUnitsValue == value) + { + return; + } + m_PositionRightUnitsValue = value; + positionRightUnitsValueChanged(); + } + + inline uint32_t positionTopUnitsValue() const { return m_PositionTopUnitsValue; } + void positionTopUnitsValue(uint32_t value) + { + if (m_PositionTopUnitsValue == value) + { + return; + } + m_PositionTopUnitsValue = value; + positionTopUnitsValueChanged(); + } + + inline uint32_t positionBottomUnitsValue() const { return m_PositionBottomUnitsValue; } + void positionBottomUnitsValue(uint32_t value) + { + if (m_PositionBottomUnitsValue == value) + { + return; + } + m_PositionBottomUnitsValue = value; + positionBottomUnitsValueChanged(); + } + + inline uint32_t gapHorizontalUnitsValue() const { return m_GapHorizontalUnitsValue; } + void gapHorizontalUnitsValue(uint32_t value) + { + if (m_GapHorizontalUnitsValue == value) + { + return; + } + m_GapHorizontalUnitsValue = value; + gapHorizontalUnitsValueChanged(); + } + + inline uint32_t gapVerticalUnitsValue() const { return m_GapVerticalUnitsValue; } + void gapVerticalUnitsValue(uint32_t value) + { + if (m_GapVerticalUnitsValue == value) + { + return; + } + m_GapVerticalUnitsValue = value; + gapVerticalUnitsValueChanged(); + } + + inline uint32_t minWidthUnitsValue() const { return m_MinWidthUnitsValue; } + void minWidthUnitsValue(uint32_t value) + { + if (m_MinWidthUnitsValue == value) + { + return; + } + m_MinWidthUnitsValue = value; + minWidthUnitsValueChanged(); + } + + inline uint32_t minHeightUnitsValue() const { return m_MinHeightUnitsValue; } + void minHeightUnitsValue(uint32_t value) + { + if (m_MinHeightUnitsValue == value) + { + return; + } + m_MinHeightUnitsValue = value; + minHeightUnitsValueChanged(); + } + + inline uint32_t maxWidthUnitsValue() const { return m_MaxWidthUnitsValue; } + void maxWidthUnitsValue(uint32_t value) + { + if (m_MaxWidthUnitsValue == value) + { + return; + } + m_MaxWidthUnitsValue = value; + maxWidthUnitsValueChanged(); + } + + inline uint32_t maxHeightUnitsValue() const { return m_MaxHeightUnitsValue; } + void maxHeightUnitsValue(uint32_t value) + { + if (m_MaxHeightUnitsValue == value) + { + return; + } + m_MaxHeightUnitsValue = value; + maxHeightUnitsValueChanged(); + } + + inline bool linkCornerRadius() const { return m_LinkCornerRadius; } + void linkCornerRadius(bool value) + { + if (m_LinkCornerRadius == value) + { + return; + } + m_LinkCornerRadius = value; + linkCornerRadiusChanged(); + } + + inline float cornerRadiusTL() const { return m_CornerRadiusTL; } + void cornerRadiusTL(float value) + { + if (m_CornerRadiusTL == value) + { + return; + } + m_CornerRadiusTL = value; + cornerRadiusTLChanged(); + } + + inline float cornerRadiusTR() const { return m_CornerRadiusTR; } + void cornerRadiusTR(float value) + { + if (m_CornerRadiusTR == value) + { + return; + } + m_CornerRadiusTR = value; + cornerRadiusTRChanged(); + } + + inline float cornerRadiusBL() const { return m_CornerRadiusBL; } + void cornerRadiusBL(float value) + { + if (m_CornerRadiusBL == value) + { + return; + } + m_CornerRadiusBL = value; + cornerRadiusBLChanged(); + } + + inline float cornerRadiusBR() const { return m_CornerRadiusBR; } + void cornerRadiusBR(float value) + { + if (m_CornerRadiusBR == value) + { + return; + } + m_CornerRadiusBR = value; + cornerRadiusBRChanged(); + } + + Core* clone() const override; + void copy(const LayoutComponentStyleBase& object) + { + m_GapHorizontal = object.m_GapHorizontal; + m_GapVertical = object.m_GapVertical; + m_MaxWidth = object.m_MaxWidth; + m_MaxHeight = object.m_MaxHeight; + m_MinWidth = object.m_MinWidth; + m_MinHeight = object.m_MinHeight; + m_BorderLeft = object.m_BorderLeft; + m_BorderRight = object.m_BorderRight; + m_BorderTop = object.m_BorderTop; + m_BorderBottom = object.m_BorderBottom; + m_MarginLeft = object.m_MarginLeft; + m_MarginRight = object.m_MarginRight; + m_MarginTop = object.m_MarginTop; + m_MarginBottom = object.m_MarginBottom; + m_PaddingLeft = object.m_PaddingLeft; + m_PaddingRight = object.m_PaddingRight; + m_PaddingTop = object.m_PaddingTop; + m_PaddingBottom = object.m_PaddingBottom; + m_PositionLeft = object.m_PositionLeft; + m_PositionRight = object.m_PositionRight; + m_PositionTop = object.m_PositionTop; + m_PositionBottom = object.m_PositionBottom; + m_Flex = object.m_Flex; + m_FlexGrow = object.m_FlexGrow; + m_FlexShrink = object.m_FlexShrink; + m_FlexBasis = object.m_FlexBasis; + m_AspectRatio = object.m_AspectRatio; + m_LayoutWidthScaleType = object.m_LayoutWidthScaleType; + m_LayoutHeightScaleType = object.m_LayoutHeightScaleType; + m_LayoutAlignmentType = object.m_LayoutAlignmentType; + m_AnimationStyleType = object.m_AnimationStyleType; + m_InterpolationType = object.m_InterpolationType; + m_InterpolatorId = object.m_InterpolatorId; + m_InterpolationTime = object.m_InterpolationTime; + m_DisplayValue = object.m_DisplayValue; + m_PositionTypeValue = object.m_PositionTypeValue; + m_FlexDirectionValue = object.m_FlexDirectionValue; + m_DirectionValue = object.m_DirectionValue; + m_AlignContentValue = object.m_AlignContentValue; + m_AlignItemsValue = object.m_AlignItemsValue; + m_AlignSelfValue = object.m_AlignSelfValue; + m_JustifyContentValue = object.m_JustifyContentValue; + m_FlexWrapValue = object.m_FlexWrapValue; + m_OverflowValue = object.m_OverflowValue; + m_IntrinsicallySizedValue = object.m_IntrinsicallySizedValue; + m_WidthUnitsValue = object.m_WidthUnitsValue; + m_HeightUnitsValue = object.m_HeightUnitsValue; + m_BorderLeftUnitsValue = object.m_BorderLeftUnitsValue; + m_BorderRightUnitsValue = object.m_BorderRightUnitsValue; + m_BorderTopUnitsValue = object.m_BorderTopUnitsValue; + m_BorderBottomUnitsValue = object.m_BorderBottomUnitsValue; + m_MarginLeftUnitsValue = object.m_MarginLeftUnitsValue; + m_MarginRightUnitsValue = object.m_MarginRightUnitsValue; + m_MarginTopUnitsValue = object.m_MarginTopUnitsValue; + m_MarginBottomUnitsValue = object.m_MarginBottomUnitsValue; + m_PaddingLeftUnitsValue = object.m_PaddingLeftUnitsValue; + m_PaddingRightUnitsValue = object.m_PaddingRightUnitsValue; + m_PaddingTopUnitsValue = object.m_PaddingTopUnitsValue; + m_PaddingBottomUnitsValue = object.m_PaddingBottomUnitsValue; + m_PositionLeftUnitsValue = object.m_PositionLeftUnitsValue; + m_PositionRightUnitsValue = object.m_PositionRightUnitsValue; + m_PositionTopUnitsValue = object.m_PositionTopUnitsValue; + m_PositionBottomUnitsValue = object.m_PositionBottomUnitsValue; + m_GapHorizontalUnitsValue = object.m_GapHorizontalUnitsValue; + m_GapVerticalUnitsValue = object.m_GapVerticalUnitsValue; + m_MinWidthUnitsValue = object.m_MinWidthUnitsValue; + m_MinHeightUnitsValue = object.m_MinHeightUnitsValue; + m_MaxWidthUnitsValue = object.m_MaxWidthUnitsValue; + m_MaxHeightUnitsValue = object.m_MaxHeightUnitsValue; + m_LinkCornerRadius = object.m_LinkCornerRadius; + m_CornerRadiusTL = object.m_CornerRadiusTL; + m_CornerRadiusTR = object.m_CornerRadiusTR; + m_CornerRadiusBL = object.m_CornerRadiusBL; + m_CornerRadiusBR = object.m_CornerRadiusBR; + Component::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case gapHorizontalPropertyKey: + m_GapHorizontal = CoreDoubleType::deserialize(reader); + return true; + case gapVerticalPropertyKey: + m_GapVertical = CoreDoubleType::deserialize(reader); + return true; + case maxWidthPropertyKey: + m_MaxWidth = CoreDoubleType::deserialize(reader); + return true; + case maxHeightPropertyKey: + m_MaxHeight = CoreDoubleType::deserialize(reader); + return true; + case minWidthPropertyKey: + m_MinWidth = CoreDoubleType::deserialize(reader); + return true; + case minHeightPropertyKey: + m_MinHeight = CoreDoubleType::deserialize(reader); + return true; + case borderLeftPropertyKey: + m_BorderLeft = CoreDoubleType::deserialize(reader); + return true; + case borderRightPropertyKey: + m_BorderRight = CoreDoubleType::deserialize(reader); + return true; + case borderTopPropertyKey: + m_BorderTop = CoreDoubleType::deserialize(reader); + return true; + case borderBottomPropertyKey: + m_BorderBottom = CoreDoubleType::deserialize(reader); + return true; + case marginLeftPropertyKey: + m_MarginLeft = CoreDoubleType::deserialize(reader); + return true; + case marginRightPropertyKey: + m_MarginRight = CoreDoubleType::deserialize(reader); + return true; + case marginTopPropertyKey: + m_MarginTop = CoreDoubleType::deserialize(reader); + return true; + case marginBottomPropertyKey: + m_MarginBottom = CoreDoubleType::deserialize(reader); + return true; + case paddingLeftPropertyKey: + m_PaddingLeft = CoreDoubleType::deserialize(reader); + return true; + case paddingRightPropertyKey: + m_PaddingRight = CoreDoubleType::deserialize(reader); + return true; + case paddingTopPropertyKey: + m_PaddingTop = CoreDoubleType::deserialize(reader); + return true; + case paddingBottomPropertyKey: + m_PaddingBottom = CoreDoubleType::deserialize(reader); + return true; + case positionLeftPropertyKey: + m_PositionLeft = CoreDoubleType::deserialize(reader); + return true; + case positionRightPropertyKey: + m_PositionRight = CoreDoubleType::deserialize(reader); + return true; + case positionTopPropertyKey: + m_PositionTop = CoreDoubleType::deserialize(reader); + return true; + case positionBottomPropertyKey: + m_PositionBottom = CoreDoubleType::deserialize(reader); + return true; + case flexPropertyKey: + m_Flex = CoreDoubleType::deserialize(reader); + return true; + case flexGrowPropertyKey: + m_FlexGrow = CoreDoubleType::deserialize(reader); + return true; + case flexShrinkPropertyKey: + m_FlexShrink = CoreDoubleType::deserialize(reader); + return true; + case flexBasisPropertyKey: + m_FlexBasis = CoreDoubleType::deserialize(reader); + return true; + case aspectRatioPropertyKey: + m_AspectRatio = CoreDoubleType::deserialize(reader); + return true; + case layoutWidthScaleTypePropertyKey: + m_LayoutWidthScaleType = CoreUintType::deserialize(reader); + return true; + case layoutHeightScaleTypePropertyKey: + m_LayoutHeightScaleType = CoreUintType::deserialize(reader); + return true; + case layoutAlignmentTypePropertyKey: + m_LayoutAlignmentType = CoreUintType::deserialize(reader); + return true; + case animationStyleTypePropertyKey: + m_AnimationStyleType = CoreUintType::deserialize(reader); + return true; + case interpolationTypePropertyKey: + m_InterpolationType = CoreUintType::deserialize(reader); + return true; + case interpolatorIdPropertyKey: + m_InterpolatorId = CoreUintType::deserialize(reader); + return true; + case interpolationTimePropertyKey: + m_InterpolationTime = CoreDoubleType::deserialize(reader); + return true; + case displayValuePropertyKey: + m_DisplayValue = CoreUintType::deserialize(reader); + return true; + case positionTypeValuePropertyKey: + m_PositionTypeValue = CoreUintType::deserialize(reader); + return true; + case flexDirectionValuePropertyKey: + m_FlexDirectionValue = CoreUintType::deserialize(reader); + return true; + case directionValuePropertyKey: + m_DirectionValue = CoreUintType::deserialize(reader); + return true; + case alignContentValuePropertyKey: + m_AlignContentValue = CoreUintType::deserialize(reader); + return true; + case alignItemsValuePropertyKey: + m_AlignItemsValue = CoreUintType::deserialize(reader); + return true; + case alignSelfValuePropertyKey: + m_AlignSelfValue = CoreUintType::deserialize(reader); + return true; + case justifyContentValuePropertyKey: + m_JustifyContentValue = CoreUintType::deserialize(reader); + return true; + case flexWrapValuePropertyKey: + m_FlexWrapValue = CoreUintType::deserialize(reader); + return true; + case overflowValuePropertyKey: + m_OverflowValue = CoreUintType::deserialize(reader); + return true; + case intrinsicallySizedValuePropertyKey: + m_IntrinsicallySizedValue = CoreBoolType::deserialize(reader); + return true; + case widthUnitsValuePropertyKey: + m_WidthUnitsValue = CoreUintType::deserialize(reader); + return true; + case heightUnitsValuePropertyKey: + m_HeightUnitsValue = CoreUintType::deserialize(reader); + return true; + case borderLeftUnitsValuePropertyKey: + m_BorderLeftUnitsValue = CoreUintType::deserialize(reader); + return true; + case borderRightUnitsValuePropertyKey: + m_BorderRightUnitsValue = CoreUintType::deserialize(reader); + return true; + case borderTopUnitsValuePropertyKey: + m_BorderTopUnitsValue = CoreUintType::deserialize(reader); + return true; + case borderBottomUnitsValuePropertyKey: + m_BorderBottomUnitsValue = CoreUintType::deserialize(reader); + return true; + case marginLeftUnitsValuePropertyKey: + m_MarginLeftUnitsValue = CoreUintType::deserialize(reader); + return true; + case marginRightUnitsValuePropertyKey: + m_MarginRightUnitsValue = CoreUintType::deserialize(reader); + return true; + case marginTopUnitsValuePropertyKey: + m_MarginTopUnitsValue = CoreUintType::deserialize(reader); + return true; + case marginBottomUnitsValuePropertyKey: + m_MarginBottomUnitsValue = CoreUintType::deserialize(reader); + return true; + case paddingLeftUnitsValuePropertyKey: + m_PaddingLeftUnitsValue = CoreUintType::deserialize(reader); + return true; + case paddingRightUnitsValuePropertyKey: + m_PaddingRightUnitsValue = CoreUintType::deserialize(reader); + return true; + case paddingTopUnitsValuePropertyKey: + m_PaddingTopUnitsValue = CoreUintType::deserialize(reader); + return true; + case paddingBottomUnitsValuePropertyKey: + m_PaddingBottomUnitsValue = CoreUintType::deserialize(reader); + return true; + case positionLeftUnitsValuePropertyKey: + m_PositionLeftUnitsValue = CoreUintType::deserialize(reader); + return true; + case positionRightUnitsValuePropertyKey: + m_PositionRightUnitsValue = CoreUintType::deserialize(reader); + return true; + case positionTopUnitsValuePropertyKey: + m_PositionTopUnitsValue = CoreUintType::deserialize(reader); + return true; + case positionBottomUnitsValuePropertyKey: + m_PositionBottomUnitsValue = CoreUintType::deserialize(reader); + return true; + case gapHorizontalUnitsValuePropertyKey: + m_GapHorizontalUnitsValue = CoreUintType::deserialize(reader); + return true; + case gapVerticalUnitsValuePropertyKey: + m_GapVerticalUnitsValue = CoreUintType::deserialize(reader); + return true; + case minWidthUnitsValuePropertyKey: + m_MinWidthUnitsValue = CoreUintType::deserialize(reader); + return true; + case minHeightUnitsValuePropertyKey: + m_MinHeightUnitsValue = CoreUintType::deserialize(reader); + return true; + case maxWidthUnitsValuePropertyKey: + m_MaxWidthUnitsValue = CoreUintType::deserialize(reader); + return true; + case maxHeightUnitsValuePropertyKey: + m_MaxHeightUnitsValue = CoreUintType::deserialize(reader); + return true; + case linkCornerRadiusPropertyKey: + m_LinkCornerRadius = CoreBoolType::deserialize(reader); + return true; + case cornerRadiusTLPropertyKey: + m_CornerRadiusTL = CoreDoubleType::deserialize(reader); + return true; + case cornerRadiusTRPropertyKey: + m_CornerRadiusTR = CoreDoubleType::deserialize(reader); + return true; + case cornerRadiusBLPropertyKey: + m_CornerRadiusBL = CoreDoubleType::deserialize(reader); + return true; + case cornerRadiusBRPropertyKey: + m_CornerRadiusBR = CoreDoubleType::deserialize(reader); + return true; + } + return Component::deserialize(propertyKey, reader); + } + +protected: + virtual void gapHorizontalChanged() {} + virtual void gapVerticalChanged() {} + virtual void maxWidthChanged() {} + virtual void maxHeightChanged() {} + virtual void minWidthChanged() {} + virtual void minHeightChanged() {} + virtual void borderLeftChanged() {} + virtual void borderRightChanged() {} + virtual void borderTopChanged() {} + virtual void borderBottomChanged() {} + virtual void marginLeftChanged() {} + virtual void marginRightChanged() {} + virtual void marginTopChanged() {} + virtual void marginBottomChanged() {} + virtual void paddingLeftChanged() {} + virtual void paddingRightChanged() {} + virtual void paddingTopChanged() {} + virtual void paddingBottomChanged() {} + virtual void positionLeftChanged() {} + virtual void positionRightChanged() {} + virtual void positionTopChanged() {} + virtual void positionBottomChanged() {} + virtual void flexChanged() {} + virtual void flexGrowChanged() {} + virtual void flexShrinkChanged() {} + virtual void flexBasisChanged() {} + virtual void aspectRatioChanged() {} + virtual void layoutWidthScaleTypeChanged() {} + virtual void layoutHeightScaleTypeChanged() {} + virtual void layoutAlignmentTypeChanged() {} + virtual void animationStyleTypeChanged() {} + virtual void interpolationTypeChanged() {} + virtual void interpolatorIdChanged() {} + virtual void interpolationTimeChanged() {} + virtual void displayValueChanged() {} + virtual void positionTypeValueChanged() {} + virtual void flexDirectionValueChanged() {} + virtual void directionValueChanged() {} + virtual void alignContentValueChanged() {} + virtual void alignItemsValueChanged() {} + virtual void alignSelfValueChanged() {} + virtual void justifyContentValueChanged() {} + virtual void flexWrapValueChanged() {} + virtual void overflowValueChanged() {} + virtual void intrinsicallySizedValueChanged() {} + virtual void widthUnitsValueChanged() {} + virtual void heightUnitsValueChanged() {} + virtual void borderLeftUnitsValueChanged() {} + virtual void borderRightUnitsValueChanged() {} + virtual void borderTopUnitsValueChanged() {} + virtual void borderBottomUnitsValueChanged() {} + virtual void marginLeftUnitsValueChanged() {} + virtual void marginRightUnitsValueChanged() {} + virtual void marginTopUnitsValueChanged() {} + virtual void marginBottomUnitsValueChanged() {} + virtual void paddingLeftUnitsValueChanged() {} + virtual void paddingRightUnitsValueChanged() {} + virtual void paddingTopUnitsValueChanged() {} + virtual void paddingBottomUnitsValueChanged() {} + virtual void positionLeftUnitsValueChanged() {} + virtual void positionRightUnitsValueChanged() {} + virtual void positionTopUnitsValueChanged() {} + virtual void positionBottomUnitsValueChanged() {} + virtual void gapHorizontalUnitsValueChanged() {} + virtual void gapVerticalUnitsValueChanged() {} + virtual void minWidthUnitsValueChanged() {} + virtual void minHeightUnitsValueChanged() {} + virtual void maxWidthUnitsValueChanged() {} + virtual void maxHeightUnitsValueChanged() {} + virtual void linkCornerRadiusChanged() {} + virtual void cornerRadiusTLChanged() {} + virtual void cornerRadiusTRChanged() {} + virtual void cornerRadiusBLChanged() {} + virtual void cornerRadiusBRChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/layout/n_slicer_base.hpp b/include/rive/generated/layout/n_slicer_base.hpp new file mode 100644 index 00000000..9ef88e90 --- /dev/null +++ b/include/rive/generated/layout/n_slicer_base.hpp @@ -0,0 +1,37 @@ +#ifndef _RIVE_N_SLICER_BASE_HPP_ +#define _RIVE_N_SLICER_BASE_HPP_ +#include "rive/container_component.hpp" +namespace rive +{ +class NSlicerBase : public ContainerComponent +{ +protected: + typedef ContainerComponent Super; + +public: + static const uint16_t typeKey = 493; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case NSlicerBase::typeKey: + case ContainerComponentBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/layout/n_slicer_tile_mode_base.hpp b/include/rive/generated/layout/n_slicer_tile_mode_base.hpp new file mode 100644 index 00000000..c3673f0f --- /dev/null +++ b/include/rive/generated/layout/n_slicer_tile_mode_base.hpp @@ -0,0 +1,89 @@ +#ifndef _RIVE_N_SLICER_TILE_MODE_BASE_HPP_ +#define _RIVE_N_SLICER_TILE_MODE_BASE_HPP_ +#include "rive/component.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class NSlicerTileModeBase : public Component +{ +protected: + typedef Component Super; + +public: + static const uint16_t typeKey = 491; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case NSlicerTileModeBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t patchIndexPropertyKey = 672; + static const uint16_t stylePropertyKey = 673; + +private: + uint32_t m_PatchIndex = 0; + uint32_t m_Style = 0; + +public: + inline uint32_t patchIndex() const { return m_PatchIndex; } + void patchIndex(uint32_t value) + { + if (m_PatchIndex == value) + { + return; + } + m_PatchIndex = value; + patchIndexChanged(); + } + + inline uint32_t style() const { return m_Style; } + void style(uint32_t value) + { + if (m_Style == value) + { + return; + } + m_Style = value; + styleChanged(); + } + + Core* clone() const override; + void copy(const NSlicerTileModeBase& object) + { + m_PatchIndex = object.m_PatchIndex; + m_Style = object.m_Style; + Component::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case patchIndexPropertyKey: + m_PatchIndex = CoreUintType::deserialize(reader); + return true; + case stylePropertyKey: + m_Style = CoreUintType::deserialize(reader); + return true; + } + return Component::deserialize(propertyKey, reader); + } + +protected: + virtual void patchIndexChanged() {} + virtual void styleChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/layout_component_base.hpp b/include/rive/generated/layout_component_base.hpp new file mode 100644 index 00000000..680f0319 --- /dev/null +++ b/include/rive/generated/layout_component_base.hpp @@ -0,0 +1,132 @@ +#ifndef _RIVE_LAYOUT_COMPONENT_BASE_HPP_ +#define _RIVE_LAYOUT_COMPONENT_BASE_HPP_ +#include "rive/core/field_types/core_bool_type.hpp" +#include "rive/core/field_types/core_double_type.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +#include "rive/drawable.hpp" +namespace rive +{ +class LayoutComponentBase : public Drawable +{ +protected: + typedef Drawable Super; + +public: + static const uint16_t typeKey = 409; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case LayoutComponentBase::typeKey: + case DrawableBase::typeKey: + case NodeBase::typeKey: + case TransformComponentBase::typeKey: + case WorldTransformComponentBase::typeKey: + case ContainerComponentBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t clipPropertyKey = 196; + static const uint16_t widthPropertyKey = 7; + static const uint16_t heightPropertyKey = 8; + static const uint16_t styleIdPropertyKey = 494; + +private: + bool m_Clip = true; + float m_Width = 0.0f; + float m_Height = 0.0f; + uint32_t m_StyleId = -1; + +public: + inline bool clip() const { return m_Clip; } + void clip(bool value) + { + if (m_Clip == value) + { + return; + } + m_Clip = value; + clipChanged(); + } + + inline float width() const { return m_Width; } + void width(float value) + { + if (m_Width == value) + { + return; + } + m_Width = value; + widthChanged(); + } + + inline float height() const { return m_Height; } + void height(float value) + { + if (m_Height == value) + { + return; + } + m_Height = value; + heightChanged(); + } + + inline uint32_t styleId() const { return m_StyleId; } + void styleId(uint32_t value) + { + if (m_StyleId == value) + { + return; + } + m_StyleId = value; + styleIdChanged(); + } + + Core* clone() const override; + void copy(const LayoutComponentBase& object) + { + m_Clip = object.m_Clip; + m_Width = object.m_Width; + m_Height = object.m_Height; + m_StyleId = object.m_StyleId; + Drawable::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case clipPropertyKey: + m_Clip = CoreBoolType::deserialize(reader); + return true; + case widthPropertyKey: + m_Width = CoreDoubleType::deserialize(reader); + return true; + case heightPropertyKey: + m_Height = CoreDoubleType::deserialize(reader); + return true; + case styleIdPropertyKey: + m_StyleId = CoreUintType::deserialize(reader); + return true; + } + return Drawable::deserialize(propertyKey, reader); + } + +protected: + virtual void clipChanged() {} + virtual void widthChanged() {} + virtual void heightChanged() {} + virtual void styleIdChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/nested_artboard_base.hpp b/include/rive/generated/nested_artboard_base.hpp index ce5d8747..a0c203ab 100644 --- a/include/rive/generated/nested_artboard_base.hpp +++ b/include/rive/generated/nested_artboard_base.hpp @@ -1,7 +1,9 @@ #ifndef _RIVE_NESTED_ARTBOARD_BASE_HPP_ #define _RIVE_NESTED_ARTBOARD_BASE_HPP_ +#include "rive/core/field_types/core_bytes_type.hpp" #include "rive/core/field_types/core_uint_type.hpp" #include "rive/drawable.hpp" +#include "rive/span.hpp" namespace rive { class NestedArtboardBase : public Drawable @@ -34,6 +36,7 @@ class NestedArtboardBase : public Drawable uint16_t coreType() const override { return typeKey; } static const uint16_t artboardIdPropertyKey = 197; + static const uint16_t dataBindPathIdsPropertyKey = 582; private: uint32_t m_ArtboardId = -1; @@ -50,10 +53,14 @@ class NestedArtboardBase : public Drawable artboardIdChanged(); } + virtual void decodeDataBindPathIds(Span value) = 0; + virtual void copyDataBindPathIds(const NestedArtboardBase& object) = 0; + Core* clone() const override; void copy(const NestedArtboardBase& object) { m_ArtboardId = object.m_ArtboardId; + copyDataBindPathIds(object); Drawable::copy(object); } @@ -64,12 +71,16 @@ class NestedArtboardBase : public Drawable case artboardIdPropertyKey: m_ArtboardId = CoreUintType::deserialize(reader); return true; + case dataBindPathIdsPropertyKey: + decodeDataBindPathIds(CoreBytesType::deserialize(reader)); + return true; } return Drawable::deserialize(propertyKey, reader); } protected: virtual void artboardIdChanged() {} + virtual void dataBindPathIdsChanged() {} }; } // namespace rive diff --git a/include/rive/generated/nested_artboard_layout_base.hpp b/include/rive/generated/nested_artboard_layout_base.hpp new file mode 100644 index 00000000..8bfa18dd --- /dev/null +++ b/include/rive/generated/nested_artboard_layout_base.hpp @@ -0,0 +1,168 @@ +#ifndef _RIVE_NESTED_ARTBOARD_LAYOUT_BASE_HPP_ +#define _RIVE_NESTED_ARTBOARD_LAYOUT_BASE_HPP_ +#include "rive/core/field_types/core_double_type.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +#include "rive/nested_artboard.hpp" +namespace rive +{ +class NestedArtboardLayoutBase : public NestedArtboard +{ +protected: + typedef NestedArtboard Super; + +public: + static const uint16_t typeKey = 452; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case NestedArtboardLayoutBase::typeKey: + case NestedArtboardBase::typeKey: + case DrawableBase::typeKey: + case NodeBase::typeKey: + case TransformComponentBase::typeKey: + case WorldTransformComponentBase::typeKey: + case ContainerComponentBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t instanceWidthPropertyKey = 663; + static const uint16_t instanceHeightPropertyKey = 664; + static const uint16_t instanceWidthUnitsValuePropertyKey = 665; + static const uint16_t instanceHeightUnitsValuePropertyKey = 666; + static const uint16_t instanceWidthScaleTypePropertyKey = 667; + static const uint16_t instanceHeightScaleTypePropertyKey = 668; + +private: + float m_InstanceWidth = -1.0f; + float m_InstanceHeight = -1.0f; + uint32_t m_InstanceWidthUnitsValue = 1; + uint32_t m_InstanceHeightUnitsValue = 1; + uint32_t m_InstanceWidthScaleType = 0; + uint32_t m_InstanceHeightScaleType = 0; + +public: + inline float instanceWidth() const { return m_InstanceWidth; } + void instanceWidth(float value) + { + if (m_InstanceWidth == value) + { + return; + } + m_InstanceWidth = value; + instanceWidthChanged(); + } + + inline float instanceHeight() const { return m_InstanceHeight; } + void instanceHeight(float value) + { + if (m_InstanceHeight == value) + { + return; + } + m_InstanceHeight = value; + instanceHeightChanged(); + } + + inline uint32_t instanceWidthUnitsValue() const { return m_InstanceWidthUnitsValue; } + void instanceWidthUnitsValue(uint32_t value) + { + if (m_InstanceWidthUnitsValue == value) + { + return; + } + m_InstanceWidthUnitsValue = value; + instanceWidthUnitsValueChanged(); + } + + inline uint32_t instanceHeightUnitsValue() const { return m_InstanceHeightUnitsValue; } + void instanceHeightUnitsValue(uint32_t value) + { + if (m_InstanceHeightUnitsValue == value) + { + return; + } + m_InstanceHeightUnitsValue = value; + instanceHeightUnitsValueChanged(); + } + + inline uint32_t instanceWidthScaleType() const { return m_InstanceWidthScaleType; } + void instanceWidthScaleType(uint32_t value) + { + if (m_InstanceWidthScaleType == value) + { + return; + } + m_InstanceWidthScaleType = value; + instanceWidthScaleTypeChanged(); + } + + inline uint32_t instanceHeightScaleType() const { return m_InstanceHeightScaleType; } + void instanceHeightScaleType(uint32_t value) + { + if (m_InstanceHeightScaleType == value) + { + return; + } + m_InstanceHeightScaleType = value; + instanceHeightScaleTypeChanged(); + } + + Core* clone() const override; + void copy(const NestedArtboardLayoutBase& object) + { + m_InstanceWidth = object.m_InstanceWidth; + m_InstanceHeight = object.m_InstanceHeight; + m_InstanceWidthUnitsValue = object.m_InstanceWidthUnitsValue; + m_InstanceHeightUnitsValue = object.m_InstanceHeightUnitsValue; + m_InstanceWidthScaleType = object.m_InstanceWidthScaleType; + m_InstanceHeightScaleType = object.m_InstanceHeightScaleType; + NestedArtboard::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case instanceWidthPropertyKey: + m_InstanceWidth = CoreDoubleType::deserialize(reader); + return true; + case instanceHeightPropertyKey: + m_InstanceHeight = CoreDoubleType::deserialize(reader); + return true; + case instanceWidthUnitsValuePropertyKey: + m_InstanceWidthUnitsValue = CoreUintType::deserialize(reader); + return true; + case instanceHeightUnitsValuePropertyKey: + m_InstanceHeightUnitsValue = CoreUintType::deserialize(reader); + return true; + case instanceWidthScaleTypePropertyKey: + m_InstanceWidthScaleType = CoreUintType::deserialize(reader); + return true; + case instanceHeightScaleTypePropertyKey: + m_InstanceHeightScaleType = CoreUintType::deserialize(reader); + return true; + } + return NestedArtboard::deserialize(propertyKey, reader); + } + +protected: + virtual void instanceWidthChanged() {} + virtual void instanceHeightChanged() {} + virtual void instanceWidthUnitsValueChanged() {} + virtual void instanceHeightUnitsValueChanged() {} + virtual void instanceWidthScaleTypeChanged() {} + virtual void instanceHeightScaleTypeChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/nested_artboard_leaf_base.hpp b/include/rive/generated/nested_artboard_leaf_base.hpp new file mode 100644 index 00000000..d72fa60b --- /dev/null +++ b/include/rive/generated/nested_artboard_leaf_base.hpp @@ -0,0 +1,114 @@ +#ifndef _RIVE_NESTED_ARTBOARD_LEAF_BASE_HPP_ +#define _RIVE_NESTED_ARTBOARD_LEAF_BASE_HPP_ +#include "rive/core/field_types/core_double_type.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +#include "rive/nested_artboard.hpp" +namespace rive +{ +class NestedArtboardLeafBase : public NestedArtboard +{ +protected: + typedef NestedArtboard Super; + +public: + static const uint16_t typeKey = 451; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case NestedArtboardLeafBase::typeKey: + case NestedArtboardBase::typeKey: + case DrawableBase::typeKey: + case NodeBase::typeKey: + case TransformComponentBase::typeKey: + case WorldTransformComponentBase::typeKey: + case ContainerComponentBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t fitPropertyKey = 538; + static const uint16_t alignmentXPropertyKey = 644; + static const uint16_t alignmentYPropertyKey = 645; + +private: + uint32_t m_Fit = 0; + float m_AlignmentX = 0.0f; + float m_AlignmentY = 0.0f; + +public: + inline uint32_t fit() const { return m_Fit; } + void fit(uint32_t value) + { + if (m_Fit == value) + { + return; + } + m_Fit = value; + fitChanged(); + } + + inline float alignmentX() const { return m_AlignmentX; } + void alignmentX(float value) + { + if (m_AlignmentX == value) + { + return; + } + m_AlignmentX = value; + alignmentXChanged(); + } + + inline float alignmentY() const { return m_AlignmentY; } + void alignmentY(float value) + { + if (m_AlignmentY == value) + { + return; + } + m_AlignmentY = value; + alignmentYChanged(); + } + + Core* clone() const override; + void copy(const NestedArtboardLeafBase& object) + { + m_Fit = object.m_Fit; + m_AlignmentX = object.m_AlignmentX; + m_AlignmentY = object.m_AlignmentY; + NestedArtboard::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case fitPropertyKey: + m_Fit = CoreUintType::deserialize(reader); + return true; + case alignmentXPropertyKey: + m_AlignmentX = CoreDoubleType::deserialize(reader); + return true; + case alignmentYPropertyKey: + m_AlignmentY = CoreDoubleType::deserialize(reader); + return true; + } + return NestedArtboard::deserialize(propertyKey, reader); + } + +protected: + virtual void fitChanged() {} + virtual void alignmentXChanged() {} + virtual void alignmentYChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/node_base.hpp b/include/rive/generated/node_base.hpp index f27da202..38c4f60d 100644 --- a/include/rive/generated/node_base.hpp +++ b/include/rive/generated/node_base.hpp @@ -32,7 +32,9 @@ class NodeBase : public TransformComponent uint16_t coreType() const override { return typeKey; } static const uint16_t xPropertyKey = 13; + static const uint16_t xArtboardPropertyKey = 9; static const uint16_t yPropertyKey = 14; + static const uint16_t yArtboardPropertyKey = 10; private: float m_X = 0.0f; diff --git a/include/rive/generated/viewmodel/data_enum_base.hpp b/include/rive/generated/viewmodel/data_enum_base.hpp new file mode 100644 index 00000000..dc5860dc --- /dev/null +++ b/include/rive/generated/viewmodel/data_enum_base.hpp @@ -0,0 +1,38 @@ +#ifndef _RIVE_DATA_ENUM_BASE_HPP_ +#define _RIVE_DATA_ENUM_BASE_HPP_ +#include "rive/core.hpp" +namespace rive +{ +class DataEnumBase : public Core +{ +protected: + typedef Core Super; + +public: + static const uint16_t typeKey = 438; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case DataEnumBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + void copy(const DataEnumBase& object) {} + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override { return false; } + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/data_enum_value_base.hpp b/include/rive/generated/viewmodel/data_enum_value_base.hpp new file mode 100644 index 00000000..53c89f92 --- /dev/null +++ b/include/rive/generated/viewmodel/data_enum_value_base.hpp @@ -0,0 +1,88 @@ +#ifndef _RIVE_DATA_ENUM_VALUE_BASE_HPP_ +#define _RIVE_DATA_ENUM_VALUE_BASE_HPP_ +#include +#include "rive/core.hpp" +#include "rive/core/field_types/core_string_type.hpp" +namespace rive +{ +class DataEnumValueBase : public Core +{ +protected: + typedef Core Super; + +public: + static const uint16_t typeKey = 445; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case DataEnumValueBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t keyPropertyKey = 578; + static const uint16_t valuePropertyKey = 579; + +private: + std::string m_Key = ""; + std::string m_Value = ""; + +public: + inline const std::string& key() const { return m_Key; } + void key(std::string value) + { + if (m_Key == value) + { + return; + } + m_Key = value; + keyChanged(); + } + + inline const std::string& value() const { return m_Value; } + void value(std::string value) + { + if (m_Value == value) + { + return; + } + m_Value = value; + valueChanged(); + } + + Core* clone() const override; + void copy(const DataEnumValueBase& object) + { + m_Key = object.m_Key; + m_Value = object.m_Value; + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case keyPropertyKey: + m_Key = CoreStringType::deserialize(reader); + return true; + case valuePropertyKey: + m_Value = CoreStringType::deserialize(reader); + return true; + } + return false; + } + +protected: + virtual void keyChanged() {} + virtual void valueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_base.hpp b/include/rive/generated/viewmodel/viewmodel_base.hpp new file mode 100644 index 00000000..e9f63b26 --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_base.hpp @@ -0,0 +1,71 @@ +#ifndef _RIVE_VIEW_MODEL_BASE_HPP_ +#define _RIVE_VIEW_MODEL_BASE_HPP_ +#include "rive/core/field_types/core_uint_type.hpp" +#include "rive/viewmodel/viewmodel_component.hpp" +namespace rive +{ +class ViewModelBase : public ViewModelComponent +{ +protected: + typedef ViewModelComponent Super; + +public: + static const uint16_t typeKey = 435; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelBase::typeKey: + case ViewModelComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t defaultInstanceIdPropertyKey = 564; + +private: + uint32_t m_DefaultInstanceId = -1; + +public: + inline uint32_t defaultInstanceId() const { return m_DefaultInstanceId; } + void defaultInstanceId(uint32_t value) + { + if (m_DefaultInstanceId == value) + { + return; + } + m_DefaultInstanceId = value; + defaultInstanceIdChanged(); + } + + Core* clone() const override; + void copy(const ViewModelBase& object) + { + m_DefaultInstanceId = object.m_DefaultInstanceId; + ViewModelComponent::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case defaultInstanceIdPropertyKey: + m_DefaultInstanceId = CoreUintType::deserialize(reader); + return true; + } + return ViewModelComponent::deserialize(propertyKey, reader); + } + +protected: + virtual void defaultInstanceIdChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_component_base.hpp b/include/rive/generated/viewmodel/viewmodel_component_base.hpp new file mode 100644 index 00000000..611ebb36 --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_component_base.hpp @@ -0,0 +1,67 @@ +#ifndef _RIVE_VIEW_MODEL_COMPONENT_BASE_HPP_ +#define _RIVE_VIEW_MODEL_COMPONENT_BASE_HPP_ +#include +#include "rive/core.hpp" +#include "rive/core/field_types/core_string_type.hpp" +namespace rive +{ +class ViewModelComponentBase : public Core +{ +protected: + typedef Core Super; + +public: + static const uint16_t typeKey = 429; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t namePropertyKey = 557; + +private: + std::string m_Name = ""; + +public: + inline const std::string& name() const { return m_Name; } + void name(std::string value) + { + if (m_Name == value) + { + return; + } + m_Name = value; + nameChanged(); + } + + Core* clone() const override; + void copy(const ViewModelComponentBase& object) { m_Name = object.m_Name; } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case namePropertyKey: + m_Name = CoreStringType::deserialize(reader); + return true; + } + return false; + } + +protected: + virtual void nameChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_instance_base.hpp b/include/rive/generated/viewmodel/viewmodel_instance_base.hpp new file mode 100644 index 00000000..21f95aa2 --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_instance_base.hpp @@ -0,0 +1,71 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_BASE_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_BASE_HPP_ +#include "rive/component.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class ViewModelInstanceBase : public Component +{ +protected: + typedef Component Super; + +public: + static const uint16_t typeKey = 437; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelInstanceBase::typeKey: + case ComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t viewModelIdPropertyKey = 566; + +private: + uint32_t m_ViewModelId = 0; + +public: + inline uint32_t viewModelId() const { return m_ViewModelId; } + void viewModelId(uint32_t value) + { + if (m_ViewModelId == value) + { + return; + } + m_ViewModelId = value; + viewModelIdChanged(); + } + + Core* clone() const override; + void copy(const ViewModelInstanceBase& object) + { + m_ViewModelId = object.m_ViewModelId; + Component::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case viewModelIdPropertyKey: + m_ViewModelId = CoreUintType::deserialize(reader); + return true; + } + return Component::deserialize(propertyKey, reader); + } + +protected: + virtual void viewModelIdChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_instance_boolean_base.hpp b/include/rive/generated/viewmodel/viewmodel_instance_boolean_base.hpp new file mode 100644 index 00000000..5841b6cc --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_instance_boolean_base.hpp @@ -0,0 +1,71 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_BOOLEAN_BASE_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_BOOLEAN_BASE_HPP_ +#include "rive/core/field_types/core_bool_type.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" +namespace rive +{ +class ViewModelInstanceBooleanBase : public ViewModelInstanceValue +{ +protected: + typedef ViewModelInstanceValue Super; + +public: + static const uint16_t typeKey = 449; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelInstanceBooleanBase::typeKey: + case ViewModelInstanceValueBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t propertyValuePropertyKey = 593; + +private: + bool m_PropertyValue = false; + +public: + inline bool propertyValue() const { return m_PropertyValue; } + void propertyValue(bool value) + { + if (m_PropertyValue == value) + { + return; + } + m_PropertyValue = value; + propertyValueChanged(); + } + + Core* clone() const override; + void copy(const ViewModelInstanceBooleanBase& object) + { + m_PropertyValue = object.m_PropertyValue; + ViewModelInstanceValue::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case propertyValuePropertyKey: + m_PropertyValue = CoreBoolType::deserialize(reader); + return true; + } + return ViewModelInstanceValue::deserialize(propertyKey, reader); + } + +protected: + virtual void propertyValueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_instance_color_base.hpp b/include/rive/generated/viewmodel/viewmodel_instance_color_base.hpp new file mode 100644 index 00000000..873f6020 --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_instance_color_base.hpp @@ -0,0 +1,71 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_COLOR_BASE_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_COLOR_BASE_HPP_ +#include "rive/core/field_types/core_color_type.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" +namespace rive +{ +class ViewModelInstanceColorBase : public ViewModelInstanceValue +{ +protected: + typedef ViewModelInstanceValue Super; + +public: + static const uint16_t typeKey = 426; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelInstanceColorBase::typeKey: + case ViewModelInstanceValueBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t propertyValuePropertyKey = 555; + +private: + int m_PropertyValue = 0xFF1D1D1D; + +public: + inline int propertyValue() const { return m_PropertyValue; } + void propertyValue(int value) + { + if (m_PropertyValue == value) + { + return; + } + m_PropertyValue = value; + propertyValueChanged(); + } + + Core* clone() const override; + void copy(const ViewModelInstanceColorBase& object) + { + m_PropertyValue = object.m_PropertyValue; + ViewModelInstanceValue::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case propertyValuePropertyKey: + m_PropertyValue = CoreColorType::deserialize(reader); + return true; + } + return ViewModelInstanceValue::deserialize(propertyKey, reader); + } + +protected: + virtual void propertyValueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_instance_enum_base.hpp b/include/rive/generated/viewmodel/viewmodel_instance_enum_base.hpp new file mode 100644 index 00000000..7d489b30 --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_instance_enum_base.hpp @@ -0,0 +1,71 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_ENUM_BASE_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_ENUM_BASE_HPP_ +#include "rive/core/field_types/core_uint_type.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" +namespace rive +{ +class ViewModelInstanceEnumBase : public ViewModelInstanceValue +{ +protected: + typedef ViewModelInstanceValue Super; + +public: + static const uint16_t typeKey = 432; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelInstanceEnumBase::typeKey: + case ViewModelInstanceValueBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t propertyValuePropertyKey = 560; + +private: + uint32_t m_PropertyValue = 0; + +public: + inline uint32_t propertyValue() const { return m_PropertyValue; } + void propertyValue(uint32_t value) + { + if (m_PropertyValue == value) + { + return; + } + m_PropertyValue = value; + propertyValueChanged(); + } + + Core* clone() const override; + void copy(const ViewModelInstanceEnumBase& object) + { + m_PropertyValue = object.m_PropertyValue; + ViewModelInstanceValue::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case propertyValuePropertyKey: + m_PropertyValue = CoreUintType::deserialize(reader); + return true; + } + return ViewModelInstanceValue::deserialize(propertyKey, reader); + } + +protected: + virtual void propertyValueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_instance_list_base.hpp b/include/rive/generated/viewmodel/viewmodel_instance_list_base.hpp new file mode 100644 index 00000000..7a30aeb5 --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_instance_list_base.hpp @@ -0,0 +1,36 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_LIST_BASE_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_LIST_BASE_HPP_ +#include "rive/viewmodel/viewmodel_instance_value.hpp" +namespace rive +{ +class ViewModelInstanceListBase : public ViewModelInstanceValue +{ +protected: + typedef ViewModelInstanceValue Super; + +public: + static const uint16_t typeKey = 441; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelInstanceListBase::typeKey: + case ViewModelInstanceValueBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_instance_list_item_base.hpp b/include/rive/generated/viewmodel/viewmodel_instance_list_item_base.hpp new file mode 100644 index 00000000..4fedbf6f --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_instance_list_item_base.hpp @@ -0,0 +1,124 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_LIST_ITEM_BASE_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_LIST_ITEM_BASE_HPP_ +#include "rive/core.hpp" +#include "rive/core/field_types/core_bool_type.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class ViewModelInstanceListItemBase : public Core +{ +protected: + typedef Core Super; + +public: + static const uint16_t typeKey = 427; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelInstanceListItemBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t useLinkedArtboardPropertyKey = 547; + static const uint16_t viewModelIdPropertyKey = 549; + static const uint16_t viewModelInstanceIdPropertyKey = 550; + static const uint16_t artboardIdPropertyKey = 551; + +private: + bool m_UseLinkedArtboard = true; + uint32_t m_ViewModelId = -1; + uint32_t m_ViewModelInstanceId = -1; + uint32_t m_ArtboardId = -1; + +public: + inline bool useLinkedArtboard() const { return m_UseLinkedArtboard; } + void useLinkedArtboard(bool value) + { + if (m_UseLinkedArtboard == value) + { + return; + } + m_UseLinkedArtboard = value; + useLinkedArtboardChanged(); + } + + inline uint32_t viewModelId() const { return m_ViewModelId; } + void viewModelId(uint32_t value) + { + if (m_ViewModelId == value) + { + return; + } + m_ViewModelId = value; + viewModelIdChanged(); + } + + inline uint32_t viewModelInstanceId() const { return m_ViewModelInstanceId; } + void viewModelInstanceId(uint32_t value) + { + if (m_ViewModelInstanceId == value) + { + return; + } + m_ViewModelInstanceId = value; + viewModelInstanceIdChanged(); + } + + inline uint32_t artboardId() const { return m_ArtboardId; } + void artboardId(uint32_t value) + { + if (m_ArtboardId == value) + { + return; + } + m_ArtboardId = value; + artboardIdChanged(); + } + + Core* clone() const override; + void copy(const ViewModelInstanceListItemBase& object) + { + m_UseLinkedArtboard = object.m_UseLinkedArtboard; + m_ViewModelId = object.m_ViewModelId; + m_ViewModelInstanceId = object.m_ViewModelInstanceId; + m_ArtboardId = object.m_ArtboardId; + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case useLinkedArtboardPropertyKey: + m_UseLinkedArtboard = CoreBoolType::deserialize(reader); + return true; + case viewModelIdPropertyKey: + m_ViewModelId = CoreUintType::deserialize(reader); + return true; + case viewModelInstanceIdPropertyKey: + m_ViewModelInstanceId = CoreUintType::deserialize(reader); + return true; + case artboardIdPropertyKey: + m_ArtboardId = CoreUintType::deserialize(reader); + return true; + } + return false; + } + +protected: + virtual void useLinkedArtboardChanged() {} + virtual void viewModelIdChanged() {} + virtual void viewModelInstanceIdChanged() {} + virtual void artboardIdChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_instance_number_base.hpp b/include/rive/generated/viewmodel/viewmodel_instance_number_base.hpp new file mode 100644 index 00000000..6df85d0f --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_instance_number_base.hpp @@ -0,0 +1,71 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_NUMBER_BASE_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_NUMBER_BASE_HPP_ +#include "rive/core/field_types/core_double_type.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" +namespace rive +{ +class ViewModelInstanceNumberBase : public ViewModelInstanceValue +{ +protected: + typedef ViewModelInstanceValue Super; + +public: + static const uint16_t typeKey = 442; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelInstanceNumberBase::typeKey: + case ViewModelInstanceValueBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t propertyValuePropertyKey = 575; + +private: + float m_PropertyValue = 0.0f; + +public: + inline float propertyValue() const { return m_PropertyValue; } + void propertyValue(float value) + { + if (m_PropertyValue == value) + { + return; + } + m_PropertyValue = value; + propertyValueChanged(); + } + + Core* clone() const override; + void copy(const ViewModelInstanceNumberBase& object) + { + m_PropertyValue = object.m_PropertyValue; + ViewModelInstanceValue::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case propertyValuePropertyKey: + m_PropertyValue = CoreDoubleType::deserialize(reader); + return true; + } + return ViewModelInstanceValue::deserialize(propertyKey, reader); + } + +protected: + virtual void propertyValueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_instance_string_base.hpp b/include/rive/generated/viewmodel/viewmodel_instance_string_base.hpp new file mode 100644 index 00000000..538c833e --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_instance_string_base.hpp @@ -0,0 +1,72 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_STRING_BASE_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_STRING_BASE_HPP_ +#include +#include "rive/core/field_types/core_string_type.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" +namespace rive +{ +class ViewModelInstanceStringBase : public ViewModelInstanceValue +{ +protected: + typedef ViewModelInstanceValue Super; + +public: + static const uint16_t typeKey = 433; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelInstanceStringBase::typeKey: + case ViewModelInstanceValueBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t propertyValuePropertyKey = 561; + +private: + std::string m_PropertyValue = ""; + +public: + inline const std::string& propertyValue() const { return m_PropertyValue; } + void propertyValue(std::string value) + { + if (m_PropertyValue == value) + { + return; + } + m_PropertyValue = value; + propertyValueChanged(); + } + + Core* clone() const override; + void copy(const ViewModelInstanceStringBase& object) + { + m_PropertyValue = object.m_PropertyValue; + ViewModelInstanceValue::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case propertyValuePropertyKey: + m_PropertyValue = CoreStringType::deserialize(reader); + return true; + } + return ViewModelInstanceValue::deserialize(propertyKey, reader); + } + +protected: + virtual void propertyValueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_instance_value_base.hpp b/include/rive/generated/viewmodel/viewmodel_instance_value_base.hpp new file mode 100644 index 00000000..3bb08bdc --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_instance_value_base.hpp @@ -0,0 +1,68 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_VALUE_BASE_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_VALUE_BASE_HPP_ +#include "rive/core.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class ViewModelInstanceValueBase : public Core +{ +protected: + typedef Core Super; + +public: + static const uint16_t typeKey = 428; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelInstanceValueBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t viewModelPropertyIdPropertyKey = 554; + +private: + uint32_t m_ViewModelPropertyId = 0; + +public: + inline uint32_t viewModelPropertyId() const { return m_ViewModelPropertyId; } + void viewModelPropertyId(uint32_t value) + { + if (m_ViewModelPropertyId == value) + { + return; + } + m_ViewModelPropertyId = value; + viewModelPropertyIdChanged(); + } + + void copy(const ViewModelInstanceValueBase& object) + { + m_ViewModelPropertyId = object.m_ViewModelPropertyId; + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case viewModelPropertyIdPropertyKey: + m_ViewModelPropertyId = CoreUintType::deserialize(reader); + return true; + } + return false; + } + +protected: + virtual void viewModelPropertyIdChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_instance_viewmodel_base.hpp b/include/rive/generated/viewmodel/viewmodel_instance_viewmodel_base.hpp new file mode 100644 index 00000000..395569a5 --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_instance_viewmodel_base.hpp @@ -0,0 +1,71 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_VIEW_MODEL_BASE_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_VIEW_MODEL_BASE_HPP_ +#include "rive/core/field_types/core_uint_type.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" +namespace rive +{ +class ViewModelInstanceViewModelBase : public ViewModelInstanceValue +{ +protected: + typedef ViewModelInstanceValue Super; + +public: + static const uint16_t typeKey = 444; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelInstanceViewModelBase::typeKey: + case ViewModelInstanceValueBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t propertyValuePropertyKey = 577; + +private: + uint32_t m_PropertyValue = 0; + +public: + inline uint32_t propertyValue() const { return m_PropertyValue; } + void propertyValue(uint32_t value) + { + if (m_PropertyValue == value) + { + return; + } + m_PropertyValue = value; + propertyValueChanged(); + } + + Core* clone() const override; + void copy(const ViewModelInstanceViewModelBase& object) + { + m_PropertyValue = object.m_PropertyValue; + ViewModelInstanceValue::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case propertyValuePropertyKey: + m_PropertyValue = CoreUintType::deserialize(reader); + return true; + } + return ViewModelInstanceValue::deserialize(propertyKey, reader); + } + +protected: + virtual void propertyValueChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_property_base.hpp b/include/rive/generated/viewmodel/viewmodel_property_base.hpp new file mode 100644 index 00000000..fd65087f --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_property_base.hpp @@ -0,0 +1,36 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_BASE_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_BASE_HPP_ +#include "rive/viewmodel/viewmodel_component.hpp" +namespace rive +{ +class ViewModelPropertyBase : public ViewModelComponent +{ +protected: + typedef ViewModelComponent Super; + +public: + static const uint16_t typeKey = 430; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelPropertyBase::typeKey: + case ViewModelComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_property_boolean_base.hpp b/include/rive/generated/viewmodel/viewmodel_property_boolean_base.hpp new file mode 100644 index 00000000..61a8c293 --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_property_boolean_base.hpp @@ -0,0 +1,37 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_BOOLEAN_BASE_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_BOOLEAN_BASE_HPP_ +#include "rive/viewmodel/viewmodel_property.hpp" +namespace rive +{ +class ViewModelPropertyBooleanBase : public ViewModelProperty +{ +protected: + typedef ViewModelProperty Super; + +public: + static const uint16_t typeKey = 448; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelPropertyBooleanBase::typeKey: + case ViewModelPropertyBase::typeKey: + case ViewModelComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_property_color_base.hpp b/include/rive/generated/viewmodel/viewmodel_property_color_base.hpp new file mode 100644 index 00000000..a24b20f1 --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_property_color_base.hpp @@ -0,0 +1,37 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_COLOR_BASE_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_COLOR_BASE_HPP_ +#include "rive/viewmodel/viewmodel_property.hpp" +namespace rive +{ +class ViewModelPropertyColorBase : public ViewModelProperty +{ +protected: + typedef ViewModelProperty Super; + +public: + static const uint16_t typeKey = 440; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelPropertyColorBase::typeKey: + case ViewModelPropertyBase::typeKey: + case ViewModelComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_property_enum_base.hpp b/include/rive/generated/viewmodel/viewmodel_property_enum_base.hpp new file mode 100644 index 00000000..63bd198b --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_property_enum_base.hpp @@ -0,0 +1,72 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_ENUM_BASE_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_ENUM_BASE_HPP_ +#include "rive/core/field_types/core_uint_type.hpp" +#include "rive/viewmodel/viewmodel_property.hpp" +namespace rive +{ +class ViewModelPropertyEnumBase : public ViewModelProperty +{ +protected: + typedef ViewModelProperty Super; + +public: + static const uint16_t typeKey = 439; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelPropertyEnumBase::typeKey: + case ViewModelPropertyBase::typeKey: + case ViewModelComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t enumIdPropertyKey = 574; + +private: + uint32_t m_EnumId = -1; + +public: + inline uint32_t enumId() const { return m_EnumId; } + void enumId(uint32_t value) + { + if (m_EnumId == value) + { + return; + } + m_EnumId = value; + enumIdChanged(); + } + + Core* clone() const override; + void copy(const ViewModelPropertyEnumBase& object) + { + m_EnumId = object.m_EnumId; + ViewModelProperty::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case enumIdPropertyKey: + m_EnumId = CoreUintType::deserialize(reader); + return true; + } + return ViewModelProperty::deserialize(propertyKey, reader); + } + +protected: + virtual void enumIdChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_property_list_base.hpp b/include/rive/generated/viewmodel/viewmodel_property_list_base.hpp new file mode 100644 index 00000000..0ced54b4 --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_property_list_base.hpp @@ -0,0 +1,37 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_LIST_BASE_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_LIST_BASE_HPP_ +#include "rive/viewmodel/viewmodel_property.hpp" +namespace rive +{ +class ViewModelPropertyListBase : public ViewModelProperty +{ +protected: + typedef ViewModelProperty Super; + +public: + static const uint16_t typeKey = 434; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelPropertyListBase::typeKey: + case ViewModelPropertyBase::typeKey: + case ViewModelComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_property_number_base.hpp b/include/rive/generated/viewmodel/viewmodel_property_number_base.hpp new file mode 100644 index 00000000..6b4055a7 --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_property_number_base.hpp @@ -0,0 +1,37 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_NUMBER_BASE_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_NUMBER_BASE_HPP_ +#include "rive/viewmodel/viewmodel_property.hpp" +namespace rive +{ +class ViewModelPropertyNumberBase : public ViewModelProperty +{ +protected: + typedef ViewModelProperty Super; + +public: + static const uint16_t typeKey = 431; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelPropertyNumberBase::typeKey: + case ViewModelPropertyBase::typeKey: + case ViewModelComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_property_string_base.hpp b/include/rive/generated/viewmodel/viewmodel_property_string_base.hpp new file mode 100644 index 00000000..49bd3aa9 --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_property_string_base.hpp @@ -0,0 +1,37 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_STRING_BASE_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_STRING_BASE_HPP_ +#include "rive/viewmodel/viewmodel_property.hpp" +namespace rive +{ +class ViewModelPropertyStringBase : public ViewModelProperty +{ +protected: + typedef ViewModelProperty Super; + +public: + static const uint16_t typeKey = 443; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelPropertyStringBase::typeKey: + case ViewModelPropertyBase::typeKey: + case ViewModelComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/generated/viewmodel/viewmodel_property_viewmodel_base.hpp b/include/rive/generated/viewmodel/viewmodel_property_viewmodel_base.hpp new file mode 100644 index 00000000..e79e25ae --- /dev/null +++ b/include/rive/generated/viewmodel/viewmodel_property_viewmodel_base.hpp @@ -0,0 +1,72 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_VIEW_MODEL_BASE_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_VIEW_MODEL_BASE_HPP_ +#include "rive/core/field_types/core_uint_type.hpp" +#include "rive/viewmodel/viewmodel_property.hpp" +namespace rive +{ +class ViewModelPropertyViewModelBase : public ViewModelProperty +{ +protected: + typedef ViewModelProperty Super; + +public: + static const uint16_t typeKey = 436; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case ViewModelPropertyViewModelBase::typeKey: + case ViewModelPropertyBase::typeKey: + case ViewModelComponentBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t viewModelReferenceIdPropertyKey = 565; + +private: + uint32_t m_ViewModelReferenceId = 0; + +public: + inline uint32_t viewModelReferenceId() const { return m_ViewModelReferenceId; } + void viewModelReferenceId(uint32_t value) + { + if (m_ViewModelReferenceId == value) + { + return; + } + m_ViewModelReferenceId = value; + viewModelReferenceIdChanged(); + } + + Core* clone() const override; + void copy(const ViewModelPropertyViewModelBase& object) + { + m_ViewModelReferenceId = object.m_ViewModelReferenceId; + ViewModelProperty::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case viewModelReferenceIdPropertyKey: + m_ViewModelReferenceId = CoreUintType::deserialize(reader); + return true; + } + return ViewModelProperty::deserialize(propertyKey, reader); + } + +protected: + virtual void viewModelReferenceIdChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/gesture_click_phase.hpp b/include/rive/gesture_click_phase.hpp new file mode 100644 index 00000000..2daf21ae --- /dev/null +++ b/include/rive/gesture_click_phase.hpp @@ -0,0 +1,12 @@ +#ifndef _RIVE_GESTURE_CLICK_PHASE_HPP_ +#define _RIVE_GESTURE_CLICK_PHASE_HPP_ +namespace rive +{ +enum class GestureClickPhase : int +{ + out = 0, + down = 1, + clicked = 2, +}; +} +#endif \ No newline at end of file diff --git a/include/rive/importers/artboard_importer.hpp b/include/rive/importers/artboard_importer.hpp index 1525803c..102bfd02 100644 --- a/include/rive/importers/artboard_importer.hpp +++ b/include/rive/importers/artboard_importer.hpp @@ -11,6 +11,7 @@ class LinearAnimation; class StateMachine; class TextValueRun; class Event; +class DataBind; class ArtboardImporter : public ImportStackObject { private: @@ -21,6 +22,7 @@ class ArtboardImporter : public ImportStackObject void addComponent(Core* object); void addAnimation(LinearAnimation* animation); void addStateMachine(StateMachine* stateMachine); + void addDataBind(DataBind* dataBind); StatusCode resolve() override; const Artboard* artboard() const { return m_Artboard; } diff --git a/include/rive/importers/backboard_importer.hpp b/include/rive/importers/backboard_importer.hpp index f2379312..cb2f9718 100644 --- a/include/rive/importers/backboard_importer.hpp +++ b/include/rive/importers/backboard_importer.hpp @@ -12,6 +12,9 @@ class NestedArtboard; class Backboard; class FileAsset; class FileAssetReferencer; +class DataConverter; +class DataBind; +class DataConverterGroupItem; class BackboardImporter : public ImportStackObject { private: @@ -20,6 +23,9 @@ class BackboardImporter : public ImportStackObject std::vector m_NestedArtboards; std::vector m_FileAssets; std::vector m_FileAssetReferencers; + std::vector m_DataConverters; + std::vector m_DataConverterReferencers; + std::vector m_DataConverterGroupItemReferencers; int m_NextArtboardId; public: @@ -29,6 +35,9 @@ class BackboardImporter : public ImportStackObject void addNestedArtboard(NestedArtboard* artboard); void addFileAsset(FileAsset* asset); void addFileAssetReferencer(FileAssetReferencer* referencer); + void addDataConverterReferencer(DataBind* referencer); + void addDataConverter(DataConverter* converter); + void addDataConverterGroupItemReferencer(DataConverterGroupItem* referencer); StatusCode resolve() override; const Backboard* backboard() const { return m_Backboard; } diff --git a/include/rive/importers/bindable_property_importer.hpp b/include/rive/importers/bindable_property_importer.hpp new file mode 100644 index 00000000..002ebe7f --- /dev/null +++ b/include/rive/importers/bindable_property_importer.hpp @@ -0,0 +1,20 @@ +#ifndef _RIVE_BINDABLE_PROPERTY_IMPORTER_HPP_ +#define _RIVE_BINDABLE_PROPERTY_IMPORTER_HPP_ + +#include "rive/importers/import_stack.hpp" + +namespace rive +{ +class Core; +class BindableProperty; +class BindablePropertyImporter : public ImportStackObject +{ +private: + BindableProperty* m_bindableProperty; + +public: + BindablePropertyImporter(BindableProperty* bindableProperty); + BindableProperty* bindableProperty() { return m_bindableProperty; } +}; +} // namespace rive +#endif \ No newline at end of file diff --git a/include/rive/importers/data_converter_group_importer.hpp b/include/rive/importers/data_converter_group_importer.hpp new file mode 100644 index 00000000..61ba8abb --- /dev/null +++ b/include/rive/importers/data_converter_group_importer.hpp @@ -0,0 +1,20 @@ +#ifndef _RIVE_DATA_CONVERTER_GROUP_IMPORTER_HPP_ +#define _RIVE_DATA_CONVERTER_GROUP_IMPORTER_HPP_ + +#include "rive/importers/import_stack.hpp" + +namespace rive +{ +class Core; +class DataConverterGroup; +class DataConverterGroupImporter : public ImportStackObject +{ +private: + DataConverterGroup* m_dataConverterGroup; + +public: + DataConverterGroupImporter(DataConverterGroup* group); + DataConverterGroup* group() { return m_dataConverterGroup; } +}; +} // namespace rive +#endif \ No newline at end of file diff --git a/include/rive/importers/enum_importer.hpp b/include/rive/importers/enum_importer.hpp new file mode 100644 index 00000000..a79b1b6f --- /dev/null +++ b/include/rive/importers/enum_importer.hpp @@ -0,0 +1,23 @@ +#ifndef _RIVE_ENUM_IMPORTER_HPP_ +#define _RIVE_ENUM_IMPORTER_HPP_ + +#include "rive/importers/import_stack.hpp" + +namespace rive +{ +class Core; +class DataEnum; +class DataEnumValue; +class EnumImporter : public ImportStackObject +{ +private: + DataEnum* m_DataEnum; + +public: + EnumImporter(DataEnum* dataEnum); + void addValue(DataEnumValue* object); + StatusCode resolve() override; + const DataEnum* dataEnum() const { return m_DataEnum; } +}; +} // namespace rive +#endif diff --git a/include/rive/importers/import_stack.hpp b/include/rive/importers/import_stack.hpp index c80986b4..5e0436f8 100644 --- a/include/rive/importers/import_stack.hpp +++ b/include/rive/importers/import_stack.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace rive { @@ -18,41 +19,36 @@ class ImportStackObject class ImportStack { -private: - std::unordered_map m_Latests; - std::vector m_LastAdded; - public: template T* latest(uint16_t coreType) { - auto itr = m_Latests.find(coreType); - if (itr == m_Latests.end()) + auto itr = m_latests.find(coreType); + if (itr == m_latests.end()) { return nullptr; } - return static_cast(itr->second); + return static_cast(itr->second.get()); } - StatusCode makeLatest(uint16_t coreType, ImportStackObject* object) + StatusCode makeLatest(uint16_t coreType, std::unique_ptr object) { // Clean up the old object in the stack. - auto itr = m_Latests.find(coreType); - if (itr != m_Latests.end()) + auto itr = m_latests.find(coreType); + if (itr != m_latests.end()) { - auto stackObject = itr->second; + auto stackObject = itr->second.get(); // Remove it from latests. - auto lastAddedItr = std::find(m_LastAdded.begin(), m_LastAdded.end(), stackObject); - if (lastAddedItr != m_LastAdded.end()) + auto lastAddedItr = std::find(m_lastAdded.begin(), m_lastAdded.end(), stackObject); + if (lastAddedItr != m_lastAdded.end()) { - m_LastAdded.erase(lastAddedItr); + m_lastAdded.erase(lastAddedItr); } StatusCode code = stackObject->resolve(); - delete stackObject; if (code != StatusCode::Ok) { - m_Latests.erase(coreType); + m_latests.erase(coreType); return code; } } @@ -60,43 +56,41 @@ class ImportStack // Set the new one. if (object == nullptr) { - m_Latests.erase(coreType); + m_latests.erase(coreType); } else { - m_Latests[coreType] = object; - m_LastAdded.push_back(object); + m_lastAdded.push_back(object.get()); + m_latests[coreType] = std::move(object); } return StatusCode::Ok; } StatusCode resolve() { - for (auto itr = m_LastAdded.rbegin(); itr != m_LastAdded.rend(); itr++) + StatusCode returnCode = StatusCode::Ok; + + // Reverse iterate the last added import stack objects and resolve them. + // If any don't resolve, capture the return code and stop resolving. + for (auto itr = m_lastAdded.rbegin(); itr != m_lastAdded.rend(); itr++) { StatusCode code = (*itr)->resolve(); - delete *itr; if (code != StatusCode::Ok) { - return code; + returnCode = code; + break; } } - m_Latests.clear(); - m_LastAdded.clear(); - return StatusCode::Ok; - } - ~ImportStack() - { - for (auto& pair : m_Latests) - { - delete pair.second; - } + m_latests.clear(); + m_lastAdded.clear(); + + return returnCode; } bool readNullObject() { - for (auto itr = m_LastAdded.rbegin(); itr != m_LastAdded.rend(); itr++) + for (auto itr = m_lastAdded.rbegin(); itr != m_lastAdded.rend(); itr++) { if ((*itr)->readNullObject()) { @@ -105,6 +99,10 @@ class ImportStack } return false; } + +private: + std::unordered_map> m_latests; + std::vector m_lastAdded; }; } // namespace rive #endif diff --git a/include/rive/importers/state_machine_importer.hpp b/include/rive/importers/state_machine_importer.hpp index f00ae454..dc082365 100644 --- a/include/rive/importers/state_machine_importer.hpp +++ b/include/rive/importers/state_machine_importer.hpp @@ -9,6 +9,7 @@ class StateMachineInput; class StateMachineLayer; class StateMachineListener; class StateMachine; +class DataBind; class StateMachineImporter : public ImportStackObject { private: @@ -21,6 +22,7 @@ class StateMachineImporter : public ImportStackObject void addLayer(std::unique_ptr); void addInput(std::unique_ptr); void addListener(std::unique_ptr); + void addDataBind(std::unique_ptr); StatusCode resolve() override; bool readNullObject() override; diff --git a/include/rive/importers/transition_viewmodel_condition_importer.hpp b/include/rive/importers/transition_viewmodel_condition_importer.hpp new file mode 100644 index 00000000..7cf92bdc --- /dev/null +++ b/include/rive/importers/transition_viewmodel_condition_importer.hpp @@ -0,0 +1,23 @@ +#ifndef _RIVE_TRANSITION_VIEWMODEL_CONDITION_IMPORTER_HPP_ +#define _RIVE_TRANSITION_VIEWMODEL_CONDITION_IMPORTER_HPP_ + +#include "rive/importers/import_stack.hpp" + +namespace rive +{ +class TransitionViewModelCondition; +class TransitionComparator; +class DataBind; + +class TransitionViewModelConditionImporter : public ImportStackObject +{ +private: + TransitionViewModelCondition* m_TransitionViewModelCondition; + +public: + TransitionViewModelConditionImporter( + TransitionViewModelCondition* transitionViewModelCondition); + void setComparator(TransitionComparator* comparator); +}; +} // namespace rive +#endif \ No newline at end of file diff --git a/include/rive/importers/viewmodel_importer.hpp b/include/rive/importers/viewmodel_importer.hpp new file mode 100644 index 00000000..8298c26c --- /dev/null +++ b/include/rive/importers/viewmodel_importer.hpp @@ -0,0 +1,24 @@ +#ifndef _RIVE_VIEWMODEL_IMPORTER_HPP_ +#define _RIVE_VIEWMODEL_IMPORTER_HPP_ + +#include "rive/importers/import_stack.hpp" + +namespace rive +{ +class ViewModel; +class ViewModelProperty; +class ViewModelInstance; + +class ViewModelImporter : public ImportStackObject +{ +private: + ViewModel* m_ViewModel; + +public: + ViewModelImporter(ViewModel* viewModel); + void addProperty(ViewModelProperty* property); + void addInstance(ViewModelInstance* value); + StatusCode resolve() override; +}; +} // namespace rive +#endif diff --git a/include/rive/importers/viewmodel_instance_importer.hpp b/include/rive/importers/viewmodel_instance_importer.hpp new file mode 100644 index 00000000..d70c8583 --- /dev/null +++ b/include/rive/importers/viewmodel_instance_importer.hpp @@ -0,0 +1,22 @@ +#ifndef _RIVE_VIEWMODEL_INSTANCE_IMPORTER_HPP_ +#define _RIVE_VIEWMODEL_INSTANCE_IMPORTER_HPP_ + +#include "rive/importers/import_stack.hpp" + +namespace rive +{ +class ViewModelInstance; +class ViewModelInstanceValue; + +class ViewModelInstanceImporter : public ImportStackObject +{ +private: + ViewModelInstance* m_ViewModelInstance; + +public: + ViewModelInstanceImporter(ViewModelInstance* viewModelInstance); + void addValue(ViewModelInstanceValue* value); + StatusCode resolve() override; +}; +} // namespace rive +#endif diff --git a/include/rive/importers/viewmodel_instance_list_importer.hpp b/include/rive/importers/viewmodel_instance_list_importer.hpp new file mode 100644 index 00000000..3833b2ec --- /dev/null +++ b/include/rive/importers/viewmodel_instance_list_importer.hpp @@ -0,0 +1,22 @@ +#ifndef _RIVE_VIEWMODEL_INSTANCE_LIST_IMPORTER_HPP_ +#define _RIVE_VIEWMODEL_INSTANCE_LIST_IMPORTER_HPP_ + +#include "rive/importers/import_stack.hpp" + +namespace rive +{ +class Core; +class ViewModelInstanceList; +class ViewModelInstanceListItem; +class ViewModelInstanceListImporter : public ImportStackObject +{ +private: + ViewModelInstanceList* m_ViewModelInstanceList; + +public: + ViewModelInstanceListImporter(ViewModelInstanceList* viewModelInstanceList); + void addItem(ViewModelInstanceListItem* listItem); + StatusCode resolve() override; +}; +} // namespace rive +#endif diff --git a/include/rive/layout/axis.hpp b/include/rive/layout/axis.hpp new file mode 100644 index 00000000..a5eef6e5 --- /dev/null +++ b/include/rive/layout/axis.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_AXIS_HPP_ +#define _RIVE_AXIS_HPP_ +#include "rive/generated/layout/axis_base.hpp" +#include +namespace rive +{ +class Axis : public AxisBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/layout/axis_x.hpp b/include/rive/layout/axis_x.hpp new file mode 100644 index 00000000..3cd9a37c --- /dev/null +++ b/include/rive/layout/axis_x.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_AXIS_X_HPP_ +#define _RIVE_AXIS_X_HPP_ +#include "rive/generated/layout/axis_x_base.hpp" +#include +namespace rive +{ +class AxisX : public AxisXBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/layout/axis_y.hpp b/include/rive/layout/axis_y.hpp new file mode 100644 index 00000000..3f38d834 --- /dev/null +++ b/include/rive/layout/axis_y.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_AXIS_Y_HPP_ +#define _RIVE_AXIS_Y_HPP_ +#include "rive/generated/layout/axis_y_base.hpp" +#include +namespace rive +{ +class AxisY : public AxisYBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/layout/layout_component_style.hpp b/include/rive/layout/layout_component_style.hpp new file mode 100644 index 00000000..cab31c99 --- /dev/null +++ b/include/rive/layout/layout_component_style.hpp @@ -0,0 +1,185 @@ +#ifndef _RIVE_LAYOUT_COMPONENT_STYLE_HPP_ +#define _RIVE_LAYOUT_COMPONENT_STYLE_HPP_ +#include "rive/generated/layout/layout_component_style_base.hpp" +#ifdef WITH_RIVE_LAYOUT +#include "yoga/Yoga.h" +#endif +#include +namespace rive +{ +enum class LayoutAnimationStyle : uint8_t +{ + none, + inherit, + custom +}; + +enum class LayoutStyleInterpolation : uint8_t +{ + hold, + linear, + cubic, + elastic +}; + +enum class LayoutAlignmentType : uint8_t +{ + topLeft, + topCenter, + topRight, + centerLeft, + center, + centerRight, + bottomLeft, + bottomCenter, + bottomRight, + spaceBetweenStart, + spaceBetweenCenter, + spaceBetweenEnd +}; + +enum class LayoutScaleType : uint8_t +{ + fixed, + fill, + hug +}; + +class KeyFrameInterpolator; +class LayoutComponentStyle : public LayoutComponentStyleBase +{ +private: +#ifdef WITH_RIVE_LAYOUT + KeyFrameInterpolator* m_interpolator; +#endif + +public: + LayoutComponentStyle() {} + +#ifdef WITH_RIVE_LAYOUT + StatusCode onAddedDirty(CoreContext* context) override; + KeyFrameInterpolator* interpolator(); + LayoutStyleInterpolation interpolation(); + LayoutAnimationStyle animationStyle(); + YGDisplay display(); + YGPositionType positionType(); + LayoutAlignmentType alignmentType(); + LayoutScaleType widthScaleType(); + LayoutScaleType heightScaleType(); + + YGFlexDirection flexDirection(); + YGDirection direction(); + YGWrap flexWrap(); + YGOverflow overflow(); + + YGAlign alignItems(); + YGAlign alignSelf(); + YGAlign alignContent(); + YGJustify justifyContent(); + bool intrinsicallySized(); + YGUnit widthUnits(); + YGUnit heightUnits(); + + YGUnit borderLeftUnits(); + YGUnit borderRightUnits(); + YGUnit borderTopUnits(); + YGUnit borderBottomUnits(); + YGUnit marginLeftUnits(); + YGUnit marginRightUnits(); + YGUnit marginTopUnits(); + YGUnit marginBottomUnits(); + YGUnit paddingLeftUnits(); + YGUnit paddingRightUnits(); + YGUnit paddingTopUnits(); + YGUnit paddingBottomUnits(); + YGUnit positionLeftUnits(); + YGUnit positionRightUnits(); + YGUnit positionTopUnits(); + YGUnit positionBottomUnits(); + + YGUnit gapHorizontalUnits(); + YGUnit gapVerticalUnits(); + YGUnit maxWidthUnits(); + YGUnit maxHeightUnits(); + YGUnit minWidthUnits(); + YGUnit minHeightUnits(); +#endif + + void markLayoutNodeDirty(); + void markLayoutStyleDirty(); + + void layoutAlignmentTypeChanged() override; + void layoutWidthScaleTypeChanged() override; + void layoutHeightScaleTypeChanged() override; + void displayValueChanged() override; + void positionTypeValueChanged() override; + void overflowValueChanged() override; + void intrinsicallySizedValueChanged() override; + void flexDirectionValueChanged() override; + void directionValueChanged() override; + void alignContentValueChanged() override; + void alignItemsValueChanged() override; + void alignSelfValueChanged() override; + void justifyContentValueChanged() override; + void flexWrapValueChanged() override; + void flexChanged() override; + void flexGrowChanged() override; + void flexShrinkChanged() override; + void flexBasisChanged() override; + void aspectRatioChanged() override; + void gapHorizontalChanged() override; + void gapVerticalChanged() override; + void maxWidthChanged() override; + void maxHeightChanged() override; + void minWidthChanged() override; + void minHeightChanged() override; + void borderLeftChanged() override; + void borderRightChanged() override; + void borderTopChanged() override; + void borderBottomChanged() override; + void marginLeftChanged() override; + void marginRightChanged() override; + void marginTopChanged() override; + void marginBottomChanged() override; + void paddingLeftChanged() override; + void paddingRightChanged() override; + void paddingTopChanged() override; + void paddingBottomChanged() override; + void positionLeftChanged() override; + void positionRightChanged() override; + void positionTopChanged() override; + void positionBottomChanged() override; + + void widthUnitsValueChanged() override; + void heightUnitsValueChanged() override; + void gapHorizontalUnitsValueChanged() override; + void gapVerticalUnitsValueChanged() override; + void maxWidthUnitsValueChanged() override; + void maxHeightUnitsValueChanged() override; + void minWidthUnitsValueChanged() override; + void minHeightUnitsValueChanged() override; + void borderLeftUnitsValueChanged() override; + void borderRightUnitsValueChanged() override; + void borderTopUnitsValueChanged() override; + void borderBottomUnitsValueChanged() override; + void marginLeftUnitsValueChanged() override; + void marginRightUnitsValueChanged() override; + void marginTopUnitsValueChanged() override; + void marginBottomUnitsValueChanged() override; + void paddingLeftUnitsValueChanged() override; + void paddingRightUnitsValueChanged() override; + void paddingTopUnitsValueChanged() override; + void paddingBottomUnitsValueChanged() override; + void positionLeftUnitsValueChanged() override; + void positionRightUnitsValueChanged() override; + void positionTopUnitsValueChanged() override; + void positionBottomUnitsValueChanged() override; + + void cornerRadiusTLChanged() override; + void cornerRadiusTRChanged() override; + void cornerRadiusBLChanged() override; + void cornerRadiusBRChanged() override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/layout/layout_measure_mode.hpp b/include/rive/layout/layout_measure_mode.hpp new file mode 100644 index 00000000..50f6eb85 --- /dev/null +++ b/include/rive/layout/layout_measure_mode.hpp @@ -0,0 +1,12 @@ +#ifndef _RIVE_LAYOUT_MEASURE_MODE_HPP_ +#define _RIVE_LAYOUT_MEASURE_MODE_HPP_ +namespace rive +{ +enum class LayoutMeasureMode : uint8_t +{ + undefined = 0, + exactly = 1, + atMost = 2 +}; +} +#endif \ No newline at end of file diff --git a/include/rive/layout/n_slicer.hpp b/include/rive/layout/n_slicer.hpp new file mode 100644 index 00000000..7f13a61b --- /dev/null +++ b/include/rive/layout/n_slicer.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_N_SLICER_HPP_ +#define _RIVE_N_SLICER_HPP_ +#include "rive/generated/layout/n_slicer_base.hpp" +#include +namespace rive +{ +class NSlicer : public NSlicerBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/layout/n_slicer_tile_mode.hpp b/include/rive/layout/n_slicer_tile_mode.hpp new file mode 100644 index 00000000..06e7cabb --- /dev/null +++ b/include/rive/layout/n_slicer_tile_mode.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_N_SLICER_TILE_MODE_HPP_ +#define _RIVE_N_SLICER_TILE_MODE_HPP_ +#include "rive/generated/layout/n_slicer_tile_mode_base.hpp" +#include +namespace rive +{ +class NSlicerTileMode : public NSlicerTileModeBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/layout_component.hpp b/include/rive/layout_component.hpp new file mode 100644 index 00000000..18d8999e --- /dev/null +++ b/include/rive/layout_component.hpp @@ -0,0 +1,169 @@ +#ifndef _RIVE_LAYOUT_COMPONENT_HPP_ +#define _RIVE_LAYOUT_COMPONENT_HPP_ +#include "rive/drawable.hpp" +#include "rive/generated/layout_component_base.hpp" +#include "rive/layout/layout_component_style.hpp" +#include "rive/layout/layout_measure_mode.hpp" +#include "rive/math/raw_path.hpp" +#include "rive/shapes/rectangle.hpp" +#include "rive/shapes/shape_paint_container.hpp" +#ifdef WITH_RIVE_LAYOUT +#include "yoga/YGNode.h" +#include "yoga/YGStyle.h" +#include "yoga/Yoga.h" +#endif + +namespace rive +{ + +class AABB; +class KeyFrameInterpolator; + +struct LayoutData +{ +#ifdef WITH_RIVE_LAYOUT + YGNode node; + YGStyle style; +#endif +}; + +struct LayoutAnimationData +{ + float elapsedSeconds = 0; + AABB fromBounds = AABB(); + AABB toBounds = AABB(); +}; + +class LayoutComponent : public LayoutComponentBase, public ProxyDrawing, public ShapePaintContainer +{ +protected: + LayoutComponentStyle* m_style = nullptr; + std::unique_ptr m_layoutData; + + float m_layoutSizeWidth = 0; + float m_layoutSizeHeight = 0; + float m_layoutLocationX = 0; + float m_layoutLocationY = 0; + + LayoutAnimationData m_animationData; + KeyFrameInterpolator* m_inheritedInterpolator; + LayoutStyleInterpolation m_inheritedInterpolation = LayoutStyleInterpolation::hold; + float m_inheritedInterpolationTime = 0; + Rectangle* m_backgroundRect = new Rectangle(); + rcp m_backgroundPath; + rcp m_clipPath; + DrawableProxy m_proxy; + + Artboard* getArtboard() override { return artboard(); } + + LayoutComponent* layoutParent() + { + auto p = parent(); + while (p != nullptr) + { + if (p->is()) + { + return p->as(); + } + p = p->parent(); + } + return nullptr; + } + +private: + float m_widthOverride = NAN; + int m_widthUnitValueOverride = -1; + float m_heightOverride = NAN; + int m_heightUnitValueOverride = -1; + bool m_parentIsRow = true; + +#ifdef WITH_RIVE_LAYOUT +protected: + YGNode& layoutNode() { return m_layoutData->node; } + YGStyle& layoutStyle() { return m_layoutData->style; } + void syncLayoutChildren(); + void propagateSizeToChildren(ContainerComponent* component); + bool applyInterpolation(double elapsedSeconds); + +protected: + void calculateLayout(); +#endif + +public: + LayoutComponentStyle* style() { return m_style; } + void style(LayoutComponentStyle* style) { m_style = style; } + + void draw(Renderer* renderer) override; + void drawProxy(Renderer* renderer) override; + Core* hitTest(HitInfo*, const Mat2D&) override; + DrawableProxy* proxy() { return &m_proxy; }; + virtual void updateRenderPath(); + void update(ComponentDirt value) override; + void onDirty(ComponentDirt value) override; + AABB layoutBounds() + { + return AABB::fromLTWH(m_layoutLocationX, + m_layoutLocationY, + m_layoutSizeWidth, + m_layoutSizeHeight); + } + AABB localBounds() const override + { + return AABB::fromLTWH(0.0f, 0.0f, m_layoutSizeWidth, m_layoutSizeHeight); + } + + // We provide a way for nested artboards (or other objects) to override this layout's + // width/height and unit values. + void widthOverride(float width, int unitValue = 1, bool isRow = true); + void heightOverride(float height, int unitValue = 1, bool isRow = true); + virtual bool canHaveOverrides() { return false; } + bool mainAxisIsRow(); + bool mainAxisIsColumn(); + +#ifdef WITH_RIVE_LAYOUT + LayoutComponent() : m_layoutData(std::unique_ptr(new LayoutData())), m_proxy(this) + { + layoutNode().getConfig()->setPointScaleFactor(0); + } + ~LayoutComponent() { delete m_backgroundRect; } + void syncStyle(); + virtual void propagateSize(); + void updateLayoutBounds(); + StatusCode onAddedDirty(CoreContext* context) override; + StatusCode onAddedClean(CoreContext* context) override; + + bool advance(double elapsedSeconds); + bool animates(); + LayoutAnimationStyle animationStyle(); + KeyFrameInterpolator* interpolator(); + LayoutStyleInterpolation interpolation(); + float interpolationTime(); + + void cascadeAnimationStyle(LayoutStyleInterpolation inheritedInterpolation, + KeyFrameInterpolator* inheritedInterpolator, + float inheritedInterpolationTime); + void setInheritedInterpolation(LayoutStyleInterpolation inheritedInterpolation, + KeyFrameInterpolator* inheritedInterpolator, + float inheritedInterpolationTime); + void clearInheritedInterpolation(); +#else + LayoutComponent() : m_layoutData(std::unique_ptr(new LayoutData())), m_proxy(this) + {} +#endif + void buildDependencies() override; + + void markLayoutNodeDirty(); + void markLayoutStyleDirty(); + void clipChanged() override; + void widthChanged() override; + void heightChanged() override; + void styleIdChanged() override; + + Vec2D measureLayout(float width, + LayoutMeasureMode widthMode, + float height, + LayoutMeasureMode heightMode) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/listener_type.hpp b/include/rive/listener_type.hpp index d79f68cc..7fab8095 100644 --- a/include/rive/listener_type.hpp +++ b/include/rive/listener_type.hpp @@ -10,6 +10,7 @@ enum class ListenerType : int up = 3, move = 4, event = 5, + click = 6, }; } #endif \ No newline at end of file diff --git a/include/rive/math/aabb.hpp b/include/rive/math/aabb.hpp index 4c79aedb..6dda2ab4 100644 --- a/include/rive/math/aabb.hpp +++ b/include/rive/math/aabb.hpp @@ -75,6 +75,12 @@ class AABB Vec2D size() const { return {width(), height()}; } Vec2D center() const { return {(minX + maxX) * 0.5f, (minY + maxY) * 0.5f}; } + bool isEmptyOrNaN() const + { + // Use "inverse" logic so we return true if either of the comparisons fail due to a NaN. + return !(width() > 0 && height() > 0); + } + AABB inset(float dx, float dy) const { AABB r = {minX + dx, minY + dy, maxX - dx, maxY - dy}; @@ -115,6 +121,8 @@ class AABB return Vec2D(width() == 0.0f ? 0.0f : (point.x - left()) * 2.0f / width() - 1.0f, (height() == 0.0f ? 0.0f : point.y - top()) * 2.0f / height() - 1.0f); } + + bool contains(Vec2D position) const; }; } // namespace rive diff --git a/include/rive/math/bit_field_loc.hpp b/include/rive/math/bit_field_loc.hpp new file mode 100644 index 00000000..7c0c983c --- /dev/null +++ b/include/rive/math/bit_field_loc.hpp @@ -0,0 +1,28 @@ +#ifndef _RIVE_BIT_FIELD_LOC_HPP_ +#define _RIVE_BIT_FIELD_LOC_HPP_ + +#include +#include +#include +#include +#include + +namespace rive +{ + +class BitFieldLoc +{ +public: + BitFieldLoc(uint32_t start, uint32_t end); + + uint32_t read(uint32_t bits); + uint32_t write(uint32_t bits, uint32_t value); + +private: + uint32_t m_start; + uint32_t m_count; + uint32_t m_mask; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/math/contour_measure.hpp b/include/rive/math/contour_measure.hpp index 8077ee68..93eea52c 100644 --- a/include/rive/math/contour_measure.hpp +++ b/include/rive/math/contour_measure.hpp @@ -97,12 +97,12 @@ class ContourMeasureIter // approximation for the curves actual length. static constexpr float kDefaultTolerance = 0.5f; - ContourMeasureIter(const RawPath& path, float tol = kDefaultTolerance) + ContourMeasureIter(const RawPath* path, float tol = kDefaultTolerance) { this->rewind(path, tol); } - void rewind(const RawPath&, float = kDefaultTolerance); + void rewind(const RawPath*, float = kDefaultTolerance); // Returns a measure object for each contour in the path // (contours with zero-length are skipped over) diff --git a/include/rive/math/mat2d.hpp b/include/rive/math/mat2d.hpp index 8576cd1e..dd1414b3 100644 --- a/include/rive/math/mat2d.hpp +++ b/include/rive/math/mat2d.hpp @@ -46,7 +46,8 @@ class Mat2D void mapPoints(Vec2D dst[], const Vec2D pts[], size_t n) const; // Computes a bounding box that would tightly contain the given points if they were to all be - // transformed by this matrix. + // transformed by this matrix, ignoring NaN values. + // Returns {0, 0, 0, 0} if the given points are empty or all NaN. AABB mapBoundingBox(const Vec2D pts[], size_t n) const; AABB mapBoundingBox(const AABB&) const; diff --git a/include/rive/math/math_types.hpp b/include/rive/math/math_types.hpp index 481c9b54..b34adc35 100644 --- a/include/rive/math/math_types.hpp +++ b/include/rive/math/math_types.hpp @@ -61,6 +61,14 @@ template Dst bit_cast(const Src& src) return dst; } +// Lossless cast function that asserts on overflow +template T lossless_numeric_cast(U u) +{ + T t = static_cast(u); + assert(static_cast(t) == u); + return t; +} + // Attempt to generate a "clz" assembly instruction. RIVE_ALWAYS_INLINE static int clz32(uint32_t x) { @@ -106,11 +114,22 @@ RIVE_ALWAYS_INLINE static uint32_t rotateleft32(uint32_t x, int y) // Returns x rounded up to the next multiple of N. // If x is already a multiple of N, returns x. -template RIVE_ALWAYS_INLINE constexpr size_t round_up_to_multiple_of(size_t x) +template RIVE_ALWAYS_INLINE constexpr T round_up_to_multiple_of(T x) { static_assert(N != 0 && (N & (N - 1)) == 0, "math::round_up_to_multiple_of<> only supports powers of 2."); - return (x + (N - 1)) & ~(N - 1); + return (x + (N - 1)) & ~static_cast(N - 1); +} + +// Behaves better with NaN than std::clamp(). (Matching simd::clamp().) +// +// Returns lo if x == NaN (but std::clamp() returns NaN). +// Returns hi if hi <= lo. +// Ignores hi and/or lo if they are NaN. +// +RIVE_ALWAYS_INLINE static float clamp(float x, float lo, float hi) +{ + return fminf(fmaxf(lo, x), hi); } } // namespace math diff --git a/include/rive/math/raw_path.hpp b/include/rive/math/raw_path.hpp index 7f277f84..df502450 100644 --- a/include/rive/math/raw_path.hpp +++ b/include/rive/math/raw_path.hpp @@ -49,7 +49,6 @@ class RawPath RawPath transform(const Mat2D&) const; void transformInPlace(const Mat2D&); - RawPath operator*(const Mat2D& mat) const { return this->transform(mat); } Span points() const { return m_Points; } Span points() { return m_Points; } diff --git a/include/rive/nested_animation.hpp b/include/rive/nested_animation.hpp index dae78774..ac14f360 100644 --- a/include/rive/nested_animation.hpp +++ b/include/rive/nested_animation.hpp @@ -1,18 +1,63 @@ #ifndef _RIVE_NESTED_ANIMATION_HPP_ #define _RIVE_NESTED_ANIMATION_HPP_ +#include "rive/event.hpp" +#include "rive/event_report.hpp" #include "rive/generated/nested_animation_base.hpp" +#include "rive/nested_artboard.hpp" #include namespace rive { class ArtboardInstance; +class NestedEventListener +{ +public: + virtual ~NestedEventListener() {} + virtual void notify(const std::vector& events, NestedArtboard* context) = 0; +}; + +class NestedEventNotifier +{ +public: + ~NestedEventNotifier() + { + m_nestedArtboard = nullptr; + m_nestedEventListeners.clear(); + } + void addNestedEventListener(NestedEventListener* listener) + { + m_nestedEventListeners.push_back(listener); + } + std::vector nestedEventListeners() { return m_nestedEventListeners; } + + void setNestedArtboard(NestedArtboard* artboard) { m_nestedArtboard = artboard; } + NestedArtboard* nestedArtboard() { return m_nestedArtboard; } + + void notifyListeners(const std::vector& events) + { + std::vector eventReports; + for (auto event : events) + { + eventReports.push_back(EventReport(event, 0)); + } + for (auto listener : m_nestedEventListeners) + { + listener->notify(eventReports, m_nestedArtboard); + } + } + +private: + NestedArtboard* m_nestedArtboard = nullptr; + std::vector m_nestedEventListeners; +}; + class NestedAnimation : public NestedAnimationBase { public: StatusCode onAddedDirty(CoreContext* context) override; // Advance animations and apply them to the artboard. - virtual void advance(float elapsedSeconds) = 0; + virtual bool advance(float elapsedSeconds) = 0; // Initialize the animation (make instances as necessary) from the // source artboard. diff --git a/include/rive/nested_artboard.hpp b/include/rive/nested_artboard.hpp index e075c863..6b977080 100644 --- a/include/rive/nested_artboard.hpp +++ b/include/rive/nested_artboard.hpp @@ -2,22 +2,30 @@ #define _RIVE_NESTED_ARTBOARD_HPP_ #include "rive/generated/nested_artboard_base.hpp" +#include "rive/data_bind/data_context.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" #include "rive/hit_info.hpp" #include "rive/span.hpp" #include namespace rive { + class ArtboardInstance; class NestedAnimation; +class NestedInput; +class NestedStateMachine; +class StateMachineInstance; class NestedArtboard : public NestedArtboardBase { - -private: +protected: Artboard* m_Artboard = nullptr; // might point to m_Instance, and might not std::unique_ptr m_Instance; // may be null std::vector m_NestedAnimations; +protected: + std::vector m_DataBindPathIdsBuffer; + public: NestedArtboard(); ~NestedArtboard() override; @@ -27,8 +35,7 @@ class NestedArtboard : public NestedArtboardBase void addNestedAnimation(NestedAnimation* nestedAnimation); void nest(Artboard* artboard); - - ArtboardInstance* artboard() { return m_Instance.get(); } + ArtboardInstance* artboardInstance() { return m_Instance.get(); } StatusCode import(ImportStack& importStack) override; Core* clone() const override; @@ -37,13 +44,29 @@ class NestedArtboard : public NestedArtboardBase bool hasNestedStateMachines() const; Span nestedAnimations(); + NestedArtboard* nestedArtboard(std::string name) const; + NestedStateMachine* stateMachine(std::string name) const; + NestedInput* input(std::string name) const; + NestedInput* input(std::string name, std::string stateMachineName) const; + + Vec2D measureLayout(float width, + LayoutMeasureMode widthMode, + float height, + LayoutMeasureMode heightMode) override; + void controlSize(Vec2D size) override; /// Convert a world space (relative to the artboard that this /// NestedArtboard is a child of) to the local space of the Artboard /// nested within. Returns true when the conversion succeeds, and false /// when one is not possible. bool worldToLocal(Vec2D world, Vec2D* local); + void syncStyleChanges(); + void decodeDataBindPathIds(Span value) override; + void copyDataBindPathIds(const NestedArtboardBase& object) override; + std::vector dataBindPathIds() { return m_DataBindPathIdsBuffer; }; + void dataContextFromInstance(ViewModelInstance* viewModelInstance, DataContext* parent); + void internalDataContext(DataContext* dataContext, DataContext* parent); }; } // namespace rive -#endif +#endif \ No newline at end of file diff --git a/include/rive/nested_artboard_layout.hpp b/include/rive/nested_artboard_layout.hpp new file mode 100644 index 00000000..f6876ea3 --- /dev/null +++ b/include/rive/nested_artboard_layout.hpp @@ -0,0 +1,35 @@ +#ifndef _RIVE_NESTED_ARTBOARD_LAYOUT_HPP_ +#define _RIVE_NESTED_ARTBOARD_LAYOUT_HPP_ +#include "rive/generated/nested_artboard_layout_base.hpp" + +namespace rive +{ +class NestedArtboardLayout : public NestedArtboardLayoutBase +{ +public: +#ifdef WITH_RIVE_LAYOUT + void* layoutNode(); +#endif + Core* clone() const override; + void markNestedLayoutDirty(); + void update(ComponentDirt value) override; + StatusCode onAddedClean(CoreContext* context) override; + + float actualInstanceWidth(); + float actualInstanceHeight(); + +protected: + void instanceWidthChanged() override; + void instanceHeightChanged() override; + void instanceWidthUnitsValueChanged() override; + void instanceHeightUnitsValueChanged() override; + void instanceWidthScaleTypeChanged() override; + void instanceHeightScaleTypeChanged() override; + +private: + void updateWidthOverride(); + void updateHeightOverride(); +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/nested_artboard_leaf.hpp b/include/rive/nested_artboard_leaf.hpp new file mode 100644 index 00000000..a7fa3d57 --- /dev/null +++ b/include/rive/nested_artboard_leaf.hpp @@ -0,0 +1,15 @@ +#ifndef _RIVE_NESTED_ARTBOARD_LEAF_HPP_ +#define _RIVE_NESTED_ARTBOARD_LEAF_HPP_ +#include "rive/generated/nested_artboard_leaf_base.hpp" +#include +namespace rive +{ +class NestedArtboardLeaf : public NestedArtboardLeafBase +{ +public: + Core* clone() const override; + void update(ComponentDirt value) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/refcnt.hpp b/include/rive/refcnt.hpp index ed2b31bd..2b0b5e46 100644 --- a/include/rive/refcnt.hpp +++ b/include/rive/refcnt.hpp @@ -35,25 +35,23 @@ template class RefCnt public: RefCnt() : m_refcnt(1) {} - ~RefCnt() { assert(this->debugging_refcnt() == 1); } - void ref() const { (void)m_refcnt.fetch_add(+1, std::memory_order_relaxed); } void unref() const { if (1 == m_refcnt.fetch_add(-1, std::memory_order_acq_rel)) { -#ifndef NDEBUG - // we restore the "1" in debug builds just to make our destructor happy - (void)m_refcnt.fetch_add(+1, std::memory_order_relaxed); -#endif - delete static_cast(this); + static_cast(this)->onRefCntReachedZero(); } } // not reliable in actual threaded scenarios, but useful (perhaps) for debugging int32_t debugging_refcnt() const { return m_refcnt.load(std::memory_order_relaxed); } +protected: + // Can be overloaded in the subclass if specialized delete behavior is required. + void onRefCntReachedZero() const { delete static_cast(this); } + private: // mutable, so can be changed even on a const object mutable std::atomic m_refcnt; diff --git a/include/rive/relative_local_asset_loader.hpp b/include/rive/relative_local_asset_loader.hpp index 6056c9c9..c4dc317a 100644 --- a/include/rive/relative_local_asset_loader.hpp +++ b/include/rive/relative_local_asset_loader.hpp @@ -36,6 +36,11 @@ class RelativeLocalAssetLoader : public FileAssetLoader { std::string filename = m_Path + asset.uniqueFilename(); FILE* fp = fopen(filename.c_str(), "rb"); + if (fp == nullptr) + { + fprintf(stderr, "Failed to find file at %s\n", filename.c_str()); + return false; + } fseek(fp, 0, SEEK_END); const size_t length = ftell(fp); diff --git a/include/rive/scene.hpp b/include/rive/scene.hpp index fd96d8e9..7d36204d 100644 --- a/include/rive/scene.hpp +++ b/include/rive/scene.hpp @@ -13,6 +13,7 @@ namespace rive { class ArtboardInstance; class Renderer; +class ViewModelInstance; class SMIInput; class SMIBool; @@ -47,6 +48,8 @@ class Scene : public KeyedCallbackReporter, public CallbackContext void draw(Renderer*); + virtual void dataContextFromInstance(ViewModelInstance* viewModelInstance); + virtual HitResult pointerDown(Vec2D); virtual HitResult pointerMove(Vec2D); virtual HitResult pointerUp(Vec2D); diff --git a/include/rive/shapes/image.hpp b/include/rive/shapes/image.hpp index 554c25b9..7d9510b7 100644 --- a/include/rive/shapes/image.hpp +++ b/include/rive/shapes/image.hpp @@ -24,6 +24,13 @@ class Image : public ImageBase, public FileAssetReferencer void setAsset(FileAsset*) override; uint32_t assetId() override; Core* clone() const override; + Vec2D measureLayout(float width, + LayoutMeasureMode widthMode, + float height, + LayoutMeasureMode heightMode) override; + void controlSize(Vec2D size) override; + float width() const; + float height() const; }; } // namespace rive diff --git a/include/rive/shapes/metrics_path.hpp b/include/rive/shapes/metrics_path.hpp deleted file mode 100644 index 7311b9cd..00000000 --- a/include/rive/shapes/metrics_path.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef _RIVE_METRICS_PATH_HPP_ -#define _RIVE_METRICS_PATH_HPP_ - -#include "rive/command_path.hpp" -#include "rive/math/contour_measure.hpp" -#include "rive/math/vec2d.hpp" -#include -#include - -namespace rive -{ - -class MetricsPath : public CommandPath -{ -private: - RawPath m_RawPath; // temporary, until we build m_Contour - rcp m_Contour; - std::vector m_Paths; - Mat2D m_ComputedLengthTransform; - float m_ComputedLength = 0; - -public: - const std::vector& paths() const { return m_Paths; } - rcp contourMeasure() const { return m_Contour; } - - void addPath(CommandPath* path, const Mat2D& transform) override; - void rewind() override; - void moveTo(float x, float y) override; - void lineTo(float x, float y) override; - void cubicTo(float ox, float oy, float ix, float iy, float x, float y) override; - void close() override; - - float length() const { return m_ComputedLength; } - - /// Add commands to the result RenderPath that will draw the segment - /// from startLength to endLength of this MetricsPath. Requires - /// computeLength be called prior to trimming. - void trim(float startLength, float endLength, bool moveTo, RawPath* result); - - /// Add this MetricsPath to a raw path with a transform. - RawPath::Iter addToRawPath(RawPath& rawPath, const Mat2D& transform) const; - ~MetricsPath() override; - -private: - float computeLength(const Mat2D& transform); -}; - -class OnlyMetricsPath : public MetricsPath -{ -public: - void fillRule(FillRule value) override {} - - RenderPath* renderPath() override - { - // Should never be used for actual rendering. - assert(false); - return nullptr; - } -}; - -class RenderMetricsPath : public MetricsPath -{ -private: - rcp m_RenderPath; - -public: - RenderMetricsPath(rcp); - RenderPath* renderPath() override { return m_RenderPath.get(); } - void addPath(CommandPath* path, const Mat2D& transform) override; - - void fillRule(FillRule value) override; - void rewind() override; - void moveTo(float x, float y) override; - void lineTo(float x, float y) override; - void cubicTo(float ox, float oy, float ix, float iy, float x, float y) override; - void close() override; -}; -} // namespace rive -#endif diff --git a/include/rive/shapes/paint/fill.hpp b/include/rive/shapes/paint/fill.hpp index 9e8df0b5..a9a0a644 100644 --- a/include/rive/shapes/paint/fill.hpp +++ b/include/rive/shapes/paint/fill.hpp @@ -1,15 +1,18 @@ #ifndef _RIVE_FILL_HPP_ #define _RIVE_FILL_HPP_ #include "rive/generated/shapes/paint/fill_base.hpp" -#include "rive/shapes/path_space.hpp" +#include "rive/shapes/path_flags.hpp" namespace rive { class Fill : public FillBase { public: RenderPaint* initRenderPaint(ShapePaintMutator* mutator) override; - PathSpace pathSpace() const override; - void draw(Renderer* renderer, CommandPath* path, RenderPaint* paint) override; + PathFlags pathFlags() const override; + void draw(Renderer* renderer, + CommandPath* path, + const RawPath* rawPath, + RenderPaint* paint) override; void applyTo(RenderPaint* renderPaint, float opacityModifier) const override; }; } // namespace rive diff --git a/include/rive/shapes/paint/shape_paint.hpp b/include/rive/shapes/paint/shape_paint.hpp index 36735807..67e9660c 100644 --- a/include/rive/shapes/paint/shape_paint.hpp +++ b/include/rive/shapes/paint/shape_paint.hpp @@ -4,7 +4,9 @@ #include "rive/renderer.hpp" #include "rive/shapes/paint/blend_mode.hpp" #include "rive/shapes/paint/shape_paint_mutator.hpp" -#include "rive/shapes/path_space.hpp" +#include "rive/shapes/path_flags.hpp" +#include "rive/math/raw_path.hpp" + namespace rive { class RenderPaint; @@ -28,11 +30,20 @@ class ShapePaint : public ShapePaintBase /// lifecycle of the RenderPaint. virtual RenderPaint* initRenderPaint(ShapePaintMutator* mutator); - virtual PathSpace pathSpace() const = 0; + virtual PathFlags pathFlags() const = 0; + bool isFlagged(PathFlags flags) const { return (int)(pathFlags() & flags) != 0x00; } - void draw(Renderer* renderer, CommandPath* path) { draw(renderer, path, renderPaint()); } + void draw(Renderer* renderer, CommandPath* path, const RawPath* rawPath = nullptr) + { + draw(renderer, path, rawPath, renderPaint()); + } - virtual void draw(Renderer* renderer, CommandPath* path, RenderPaint* paint) = 0; + virtual void draw(Renderer* renderer, + CommandPath* path, + // When every CommandPath stores a RawPath we can get rid + // of this argument. + const RawPath* rawPath, + RenderPaint* paint) = 0; RenderPaint* renderPaint() { return m_RenderPaint.get(); } diff --git a/include/rive/shapes/paint/stroke.hpp b/include/rive/shapes/paint/stroke.hpp index 592eb8ff..99a7a0a8 100644 --- a/include/rive/shapes/paint/stroke.hpp +++ b/include/rive/shapes/paint/stroke.hpp @@ -1,7 +1,7 @@ #ifndef _RIVE_STROKE_HPP_ #define _RIVE_STROKE_HPP_ #include "rive/generated/shapes/paint/stroke_base.hpp" -#include "rive/shapes/path_space.hpp" +#include "rive/shapes/path_flags.hpp" namespace rive { class StrokeEffect; @@ -12,8 +12,11 @@ class Stroke : public StrokeBase public: RenderPaint* initRenderPaint(ShapePaintMutator* mutator) override; - PathSpace pathSpace() const override; - void draw(Renderer* renderer, CommandPath* path, RenderPaint* paint) override; + PathFlags pathFlags() const override; + void draw(Renderer* renderer, + CommandPath* path, + const RawPath* rawPath, + RenderPaint* paint) override; void addStrokeEffect(StrokeEffect* effect); bool hasStrokeEffect() { return m_Effect != nullptr; } void invalidateEffects(); diff --git a/include/rive/shapes/paint/stroke_effect.hpp b/include/rive/shapes/paint/stroke_effect.hpp index 2276d947..72ed7165 100644 --- a/include/rive/shapes/paint/stroke_effect.hpp +++ b/include/rive/shapes/paint/stroke_effect.hpp @@ -7,13 +7,13 @@ namespace rive { class Factory; class RenderPath; -class MetricsPath; +class RawPath; class StrokeEffect { public: virtual ~StrokeEffect() {} - virtual RenderPath* effectPath(MetricsPath* source, Factory*) = 0; + virtual RenderPath* effectPath(const RawPath& source, Factory*) = 0; virtual void invalidateEffect() = 0; }; } // namespace rive diff --git a/include/rive/shapes/paint/trim_path.hpp b/include/rive/shapes/paint/trim_path.hpp index 3c55ffb9..150af984 100644 --- a/include/rive/shapes/paint/trim_path.hpp +++ b/include/rive/shapes/paint/trim_path.hpp @@ -3,24 +3,43 @@ #include "rive/generated/shapes/paint/trim_path_base.hpp" #include "rive/shapes/paint/stroke_effect.hpp" #include "rive/renderer.hpp" +#include "rive/math/raw_path.hpp" +#include "rive/math/contour_measure.hpp" namespace rive { -class TrimPath : public TrimPathBase, public StrokeEffect +enum class TrimPathMode : uint8_t { -private: - rcp m_TrimmedPath; - RenderPath* m_RenderPath = nullptr; + sequential = 1, + synchronized = 2 + +}; +class TrimPath : public TrimPathBase, public StrokeEffect +{ public: StatusCode onAddedClean(CoreContext* context) override; - RenderPath* effectPath(MetricsPath* source, Factory*) override; + RenderPath* effectPath(const RawPath& source, Factory*) override; void invalidateEffect() override; void startChanged() override; void endChanged() override; void offsetChanged() override; void modeValueChanged() override; + + TrimPathMode mode() const { return (TrimPathMode)modeValue(); } + + StatusCode onAddedDirty(CoreContext* context) override; + + const RawPath& rawPath() const { return m_rawPath; } + +private: + void invalidateTrim(); + void trimRawPath(const RawPath& source); + RawPath m_rawPath; + std::vector> m_contours; + rcp m_trimmedPath; + RenderPath* m_renderPath = nullptr; }; } // namespace rive diff --git a/include/rive/shapes/parametric_path.hpp b/include/rive/shapes/parametric_path.hpp index 03f872ac..a7469fe9 100644 --- a/include/rive/shapes/parametric_path.hpp +++ b/include/rive/shapes/parametric_path.hpp @@ -1,10 +1,19 @@ #ifndef _RIVE_PARAMETRIC_PATH_HPP_ #define _RIVE_PARAMETRIC_PATH_HPP_ +#include "rive/math/aabb.hpp" #include "rive/generated/shapes/parametric_path_base.hpp" namespace rive { class ParametricPath : public ParametricPathBase { +public: + Vec2D measureLayout(float width, + LayoutMeasureMode widthMode, + float height, + LayoutMeasureMode heightMode) override; + void controlSize(Vec2D size) override; + void markPathDirty(bool sendToLayout = true) override; + protected: void widthChanged() override; void heightChanged() override; diff --git a/include/rive/shapes/path.hpp b/include/rive/shapes/path.hpp index b92428a0..0984138a 100644 --- a/include/rive/shapes/path.hpp +++ b/include/rive/shapes/path.hpp @@ -3,6 +3,7 @@ #include "rive/command_path.hpp" #include "rive/generated/shapes/path_base.hpp" #include "rive/math/mat2d.hpp" +#include "rive/math/raw_path.hpp" #include "rive/shapes/shape_paint_container.hpp" #include @@ -37,10 +38,10 @@ class Path : public PathBase { protected: Shape* m_Shape = nullptr; - rcp m_CommandPath; std::vector m_Vertices; bool m_deferredPathDirt = false; - PathSpace m_DefaultPathSpace = PathSpace::Neither; + PathFlags m_pathFlags = PathFlags::none; + RawPath m_rawPath; public: Shape* shape() const { return m_Shape; } @@ -48,13 +49,16 @@ class Path : public PathBase void buildDependencies() override; virtual const Mat2D& pathTransform() const; bool collapse(bool value) override; - CommandPath* commandPath() const { return m_CommandPath.get(); } + const RawPath& rawPath() const { return m_rawPath; } void update(ComponentDirt value) override; - void addDefaultPathSpace(PathSpace space); + void addFlags(PathFlags flags); + bool isFlagged(PathFlags flags) const; + + bool canDeferPathUpdate(); void addVertex(PathVertex* vertex); - virtual void markPathDirty(); + virtual void markPathDirty(bool sendToLayout = true); virtual bool isPathClosed() const { return true; } void onDirty(ComponentDirt dirt) override; inline bool isHidden() const { return (pathFlags() & 0x1) == 0x1; } @@ -66,8 +70,7 @@ class Path : public PathBase std::vector& vertices() { return m_Vertices; } #endif - // pour ourselves into a command-path - void buildPath(CommandPath&) const; + void buildPath(RawPath&) const; }; } // namespace rive diff --git a/include/rive/shapes/path_composer.hpp b/include/rive/shapes/path_composer.hpp index aa6ccbc8..b9b3c8fd 100644 --- a/include/rive/shapes/path_composer.hpp +++ b/include/rive/shapes/path_composer.hpp @@ -2,6 +2,8 @@ #define _RIVE_PATH_COMPOSER_HPP_ #include "rive/component.hpp" #include "rive/refcnt.hpp" +#include "rive/math/raw_path.hpp" + namespace rive { class Shape; @@ -9,23 +11,29 @@ class CommandPath; class RenderPath; class PathComposer : public Component { -private: - Shape* m_Shape; - rcp m_LocalPath; - rcp m_WorldPath; - bool m_deferredPathDirt; public: PathComposer(Shape* shape); - Shape* shape() const { return m_Shape; } + Shape* shape() const { return m_shape; } void buildDependencies() override; void onDirty(ComponentDirt dirt) override; void update(ComponentDirt value) override; - CommandPath* localPath() const { return m_LocalPath.get(); } - CommandPath* worldPath() const { return m_WorldPath.get(); } + CommandPath* localPath() const { return m_localPath.get(); } + CommandPath* worldPath() const { return m_worldPath.get(); } + + const RawPath& localRawPath() const { return m_localRawPath; } + const RawPath& worldRawPath() const { return m_worldRawPath; } void pathCollapseChanged(); + +private: + Shape* m_shape; + RawPath m_localRawPath; + RawPath m_worldRawPath; + rcp m_localPath; + rcp m_worldPath; + bool m_deferredPathDirt; }; } // namespace rive #endif diff --git a/include/rive/shapes/path_flags.hpp b/include/rive/shapes/path_flags.hpp new file mode 100644 index 00000000..172ed424 --- /dev/null +++ b/include/rive/shapes/path_flags.hpp @@ -0,0 +1,66 @@ +#ifndef _RIVE_PATH_FLAGS_HPP_ +#define _RIVE_PATH_FLAGS_HPP_ + +#include "rive/rive_types.hpp" + +namespace rive +{ +enum class PathFlags : uint8_t +{ + none = 0, + local = 1 << 1, + world = 1 << 2, + clipping = 1 << 3, + followPath = 1 << 4, + neverDeferUpdate = 1 << 5, +}; + +inline constexpr PathFlags operator&(PathFlags lhs, PathFlags rhs) +{ + return static_cast(static_cast::type>(lhs) & + static_cast::type>(rhs)); +} + +inline constexpr PathFlags operator^(PathFlags lhs, PathFlags rhs) +{ + return static_cast(static_cast::type>(lhs) ^ + static_cast::type>(rhs)); +} + +inline constexpr PathFlags operator|(PathFlags lhs, PathFlags rhs) +{ + return static_cast(static_cast::type>(lhs) | + static_cast::type>(rhs)); +} + +inline constexpr PathFlags operator~(PathFlags rhs) +{ + return static_cast(~static_cast::type>(rhs)); +} + +inline PathFlags& operator|=(PathFlags& lhs, PathFlags rhs) +{ + lhs = static_cast(static_cast::type>(lhs) | + static_cast::type>(rhs)); + + return lhs; +} + +inline PathFlags& operator&=(PathFlags& lhs, PathFlags rhs) +{ + lhs = static_cast(static_cast::type>(lhs) & + static_cast::type>(rhs)); + + return lhs; +} + +inline PathFlags& operator^=(PathFlags& lhs, PathFlags rhs) +{ + lhs = static_cast(static_cast::type>(lhs) ^ + static_cast::type>(rhs)); + + return lhs; +} +} // namespace rive + +#endif diff --git a/include/rive/shapes/path_space.hpp b/include/rive/shapes/path_space.hpp deleted file mode 100644 index d44e5c74..00000000 --- a/include/rive/shapes/path_space.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef _RIVE_PATH_SPACE_HPP_ -#define _RIVE_PATH_SPACE_HPP_ - -#include "rive/rive_types.hpp" - -namespace rive -{ -enum class PathSpace : unsigned char -{ - Neither = 0, - Local = 1 << 1, - World = 1 << 2, - Clipping = 1 << 3, - FollowPath = 1 << 4 -}; - -inline constexpr PathSpace operator&(PathSpace lhs, PathSpace rhs) -{ - return static_cast(static_cast::type>(lhs) & - static_cast::type>(rhs)); -} - -inline constexpr PathSpace operator^(PathSpace lhs, PathSpace rhs) -{ - return static_cast(static_cast::type>(lhs) ^ - static_cast::type>(rhs)); -} - -inline constexpr PathSpace operator|(PathSpace lhs, PathSpace rhs) -{ - return static_cast(static_cast::type>(lhs) | - static_cast::type>(rhs)); -} - -inline constexpr PathSpace operator~(PathSpace rhs) -{ - return static_cast(~static_cast::type>(rhs)); -} - -inline PathSpace& operator|=(PathSpace& lhs, PathSpace rhs) -{ - lhs = static_cast(static_cast::type>(lhs) | - static_cast::type>(rhs)); - - return lhs; -} - -inline PathSpace& operator&=(PathSpace& lhs, PathSpace rhs) -{ - lhs = static_cast(static_cast::type>(lhs) & - static_cast::type>(rhs)); - - return lhs; -} - -inline PathSpace& operator^=(PathSpace& lhs, PathSpace rhs) -{ - lhs = static_cast(static_cast::type>(lhs) ^ - static_cast::type>(rhs)); - - return lhs; -} -} // namespace rive - -#endif diff --git a/include/rive/shapes/points_path.hpp b/include/rive/shapes/points_path.hpp index fd91881c..743bf9ca 100644 --- a/include/rive/shapes/points_path.hpp +++ b/include/rive/shapes/points_path.hpp @@ -10,7 +10,7 @@ class PointsPath : public PointsPathBase, public Skinnable bool isPathClosed() const override { return isClosed(); } void buildDependencies() override; void update(ComponentDirt value) override; - void markPathDirty() override; + void markPathDirty(bool sendToLayout = true) override; void markSkinDirty() override; const Mat2D& pathTransform() const override; }; diff --git a/include/rive/shapes/shape.hpp b/include/rive/shapes/shape.hpp index 3f2c6869..cfb2d26a 100644 --- a/include/rive/shapes/shape.hpp +++ b/include/rive/shapes/shape.hpp @@ -5,6 +5,7 @@ #include "rive/generated/shapes/shape_base.hpp" #include "rive/shapes/path_composer.hpp" #include "rive/shapes/shape_paint_container.hpp" +#include "rive/drawable_flag.hpp" #include namespace rive @@ -17,6 +18,7 @@ class Shape : public ShapeBase, public ShapePaintContainer private: PathComposer m_PathComposer; std::vector m_Paths; + AABB m_WorldBounds; bool m_WantDifferencePath = false; @@ -42,13 +44,35 @@ class Shape : public ShapeBase, public ShapePaintContainer PathComposer* pathComposer() { return &m_PathComposer; } void pathChanged(); - void addDefaultPathSpace(PathSpace space); + void addFlags(PathFlags flags); + bool isFlagged(PathFlags flags) const; StatusCode onAddedDirty(CoreContext* context) override; bool isEmpty(); void pathCollapseChanged(); + AABB worldBounds() + { + if ((static_cast(drawableFlags()) & DrawableFlag::WorldBoundsClean) != + DrawableFlag::WorldBoundsClean) + { + drawableFlags(drawableFlags() | + static_cast(DrawableFlag::WorldBoundsClean)); + m_WorldBounds = computeWorldBounds(); + } + return m_WorldBounds; + } + void markBoundsDirty() + { + drawableFlags(drawableFlags() & + ~static_cast(DrawableFlag::WorldBoundsClean)); + } + AABB computeWorldBounds(const Mat2D* xform = nullptr) const; AABB computeLocalBounds() const; + Vec2D measureLayout(float width, + LayoutMeasureMode widthMode, + float height, + LayoutMeasureMode heightMode) override; }; } // namespace rive diff --git a/include/rive/shapes/shape_paint_container.hpp b/include/rive/shapes/shape_paint_container.hpp index 690cccae..8d394ce8 100644 --- a/include/rive/shapes/shape_paint_container.hpp +++ b/include/rive/shapes/shape_paint_container.hpp @@ -1,7 +1,7 @@ #ifndef _RIVE_SHAPE_PAINT_CONTAINER_HPP_ #define _RIVE_SHAPE_PAINT_CONTAINER_HPP_ #include "rive/refcnt.hpp" -#include "rive/shapes/path_space.hpp" +#include "rive/shapes/path_flags.hpp" #include namespace rive @@ -21,7 +21,7 @@ class ShapePaintContainer // as a Shape or Artboard, so both of those will override this. virtual Artboard* getArtboard() = 0; - PathSpace m_DefaultPathSpace = PathSpace::Neither; + PathFlags m_pathFlags = PathFlags::none; std::vector m_ShapePaints; void addPaint(ShapePaint* paint); @@ -31,12 +31,10 @@ class ShapePaintContainer virtual ~ShapePaintContainer() {} - PathSpace pathSpace() const; + PathFlags pathFlags() const; void invalidateStrokeEffects(); - rcp makeCommandPath(PathSpace space); - void propagateOpacity(float opacity); #ifdef TESTING diff --git a/include/rive/text/font_hb.hpp b/include/rive/text/font_hb.hpp index 95346110..ea112b6e 100644 --- a/include/rive/text/font_hb.hpp +++ b/include/rive/text/font_hb.hpp @@ -32,6 +32,7 @@ class HBFont : public rive::Font bool hasGlyph(rive::Span) const override; static rive::rcp Decode(rive::Span); + static rive::rcp FromSystem(void* systemFont); hb_font_t* font() const { return m_font; } private: diff --git a/include/rive/text/raw_text.hpp b/include/rive/text/raw_text.hpp new file mode 100644 index 00000000..2d76fb8c --- /dev/null +++ b/include/rive/text/raw_text.hpp @@ -0,0 +1,96 @@ +#ifndef _RIVE_RENDER_TEXT_HPP_ +#define _RIVE_RENDER_TEXT_HPP_ + +#ifdef WITH_RIVE_TEXT + +#include "rive/text/text.hpp" + +namespace rive +{ +class Factory; + +class RawText +{ +public: + RawText(Factory* factory); + + /// Returns true if the text object contains no text. + bool empty() const; + + /// Appends a run to the text object. + void append(const std::string& text, + rcp paint, + rcp font, + float size = 16.0f, + float lineHeight = -1.0f, + float letterSpacing = 0.0f); + + /// Resets the text object to empty state (no text). + void clear(); + + /// Draw the text using renderer. Second argument is optional to override + /// all paints provided with run styles + void render(Renderer* renderer, rcp paint = nullptr); + + TextSizing sizing() const; + TextOverflow overflow() const; + TextAlign align() const; + float maxWidth() const; + float maxHeight() const; + float paragraphSpacing() const; + + void sizing(TextSizing value); + + /// How text that overflows when TextSizing::fixed is used. + void overflow(TextOverflow value); + + /// How text aligns within the bounds. + void align(TextAlign value); + + /// The width at which the text will wrap when using any sizing but TextSizing::auto. + void maxWidth(float value); + + /// The height at which the text will overflow when using TextSizing::fixed. + void maxHeight(float value); + + /// The vertical space between paragraphs delineated by a return character. + void paragraphSpacing(float value); + + /// Returns the bounds of the text object (helpful for aligning multiple + /// text objects/procredurally drawn shapes). + AABB bounds(); + +private: + void update(); + struct RenderStyle + { + rcp paint; + rcp path; + bool isEmpty; + }; + SimpleArray m_shape; + SimpleArray> m_lines; + + StyledText m_styled; + Factory* m_factory; + std::vector m_styles; + std::vector m_renderStyles; + bool m_dirty = false; + float m_paragraphSpacing = 0.0f; + + TextOrigin m_origin = TextOrigin::top; + TextSizing m_sizing = TextSizing::autoWidth; + TextOverflow m_overflow = TextOverflow::visible; + TextAlign m_align = TextAlign::left; + float m_maxWidth = 0.0f; + float m_maxHeight = 0.0f; + std::vector m_orderedLines; + GlyphRun m_ellipsisRun; + AABB m_bounds; + rcp m_clipRenderPath; +}; +} // namespace rive + +#endif // WITH_RIVE_TEXT + +#endif diff --git a/include/rive/text/text.hpp b/include/rive/text/text.hpp index bed19f2d..b0267aba 100644 --- a/include/rive/text/text.hpp +++ b/include/rive/text/text.hpp @@ -1,6 +1,7 @@ #ifndef _RIVE_TEXT_CORE_HPP_ #define _RIVE_TEXT_CORE_HPP_ #include "rive/generated/text/text_base.hpp" +#include "rive/math/aabb.hpp" #include "rive/text/text_value_run.hpp" #include "rive/text_engine.hpp" #include "rive/simple_array.hpp" @@ -170,12 +171,16 @@ class Text : public TextBase Core* hitTest(HitInfo*, const Mat2D&) override; void addRun(TextValueRun* run); void addModifierGroup(TextModifierGroup* group); - void markShapeDirty(); + void markShapeDirty(bool sendToLayout = true); void modifierShapeDirty(); void markPaintDirty(); void update(ComponentDirt value) override; TextSizing sizing() const { return (TextSizing)sizingValue(); } + TextSizing effectiveSizing() const + { + return std::isnan(m_layoutHeight) ? sizing() : TextSizing::fixed; + } TextOverflow overflow() const { return (TextOverflow)overflowValue(); } TextOrigin textOrigin() const { return (TextOrigin)originValue(); } void overflow(TextOverflow value) { return overflowValue((uint32_t)value); } @@ -185,8 +190,19 @@ class Text : public TextBase AABB localBounds() const override; void originXChanged() override; void originYChanged() override; + + Vec2D measureLayout(float width, + LayoutMeasureMode widthMode, + float height, + LayoutMeasureMode heightMode) override; + void controlSize(Vec2D size) override; + float effectiveWidth() { return std::isnan(m_layoutWidth) ? width() : m_layoutWidth; } + float effectiveHeight() { return std::isnan(m_layoutHeight) ? height() : m_layoutHeight; } #ifdef WITH_RIVE_TEXT const std::vector& runs() const { return m_runs; } + static SimpleArray> BreakLines(const SimpleArray& paragraphs, + float width, + TextAlign align); #endif bool haveModifiers() const @@ -235,6 +251,9 @@ class Text : public TextBase GlyphLookup m_glyphLookup; #endif + float m_layoutWidth = NAN; + float m_layoutHeight = NAN; + Vec2D measure(Vec2D maxSize); }; } // namespace rive diff --git a/include/rive/text_engine.hpp b/include/rive/text_engine.hpp index d244f045..a0ccd637 100644 --- a/include/rive/text_engine.hpp +++ b/include/rive/text_engine.hpp @@ -169,6 +169,7 @@ class Font : public RefCnt using FallbackProc = rive::rcp (*)(rive::Span); static FallbackProc gFallbackProc; + static bool gFallbackProcEnabled; protected: Font(const LineMetrics& lm) : m_lineMetrics(lm) {} diff --git a/include/rive/transform_component.hpp b/include/rive/transform_component.hpp index 8b13bce0..36580a66 100644 --- a/include/rive/transform_component.hpp +++ b/include/rive/transform_component.hpp @@ -1,7 +1,9 @@ #ifndef _RIVE_TRANSFORM_COMPONENT_HPP_ #define _RIVE_TRANSFORM_COMPONENT_HPP_ #include "rive/generated/transform_component_base.hpp" +#include "rive/math/aabb.hpp" #include "rive/math/mat2d.hpp" +#include "rive/layout/layout_measure_mode.hpp" namespace rive { @@ -10,20 +12,23 @@ class WorldTransformComponent; class AABB; class TransformComponent : public TransformComponentBase { -private: +protected: Mat2D m_Transform; float m_RenderOpacity = 0.0f; WorldTransformComponent* m_ParentTransformComponent = nullptr; std::vector m_Constraints; +protected: + void updateConstraints(); + public: bool collapse(bool value) override; const std::vector& constraints() const { return m_Constraints; } StatusCode onAddedClean(CoreContext* context) override; void buildDependencies() override; void update(ComponentDirt value) override; - void updateTransform(); - void updateWorldTransform(); + virtual void updateTransform(); + virtual void updateWorldTransform(); void markTransformDirty(); /// Opacity inherited by any child of this transform component. This'll @@ -47,6 +52,16 @@ class TransformComponent : public TransformComponentBase void addConstraint(Constraint* constraint); virtual AABB localBounds() const; void markDirtyIfConstrained(); + + virtual Vec2D measureLayout(float width, + LayoutMeasureMode widthMode, + float height, + LayoutMeasureMode heightMode) + { + return Vec2D(); + } + + virtual void controlSize(Vec2D size) {} }; } // namespace rive diff --git a/include/rive/viewmodel/data_enum.hpp b/include/rive/viewmodel/data_enum.hpp new file mode 100644 index 00000000..ed8addb6 --- /dev/null +++ b/include/rive/viewmodel/data_enum.hpp @@ -0,0 +1,25 @@ +#ifndef _RIVE_DATA_ENUM_HPP_ +#define _RIVE_DATA_ENUM_HPP_ +#include "rive/generated/viewmodel/data_enum_base.hpp" +#include "rive/viewmodel/data_enum_value.hpp" +#include +namespace rive +{ +class DataEnum : public DataEnumBase +{ +private: + std::vector m_Values; + +public: + void addValue(DataEnumValue* value); + std::vector values() { return m_Values; }; + std::string value(std::string name); + std::string value(uint32_t index); + bool value(std::string name, std::string value); + bool value(uint32_t index, std::string value); + int valueIndex(std::string name); + int valueIndex(uint32_t index); +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/data_enum_value.hpp b/include/rive/viewmodel/data_enum_value.hpp new file mode 100644 index 00000000..de642162 --- /dev/null +++ b/include/rive/viewmodel/data_enum_value.hpp @@ -0,0 +1,14 @@ +#ifndef _RIVE_DATA_ENUM_VALUE_HPP_ +#define _RIVE_DATA_ENUM_VALUE_HPP_ +#include "rive/generated/viewmodel/data_enum_value_base.hpp" +#include +namespace rive +{ +class DataEnumValue : public DataEnumValueBase +{ +public: + StatusCode import(ImportStack& importStack) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel.hpp b/include/rive/viewmodel/viewmodel.hpp new file mode 100644 index 00000000..2dbc6002 --- /dev/null +++ b/include/rive/viewmodel/viewmodel.hpp @@ -0,0 +1,26 @@ +#ifndef _RIVE_VIEW_MODEL_HPP_ +#define _RIVE_VIEW_MODEL_HPP_ +#include "rive/generated/viewmodel/viewmodel_base.hpp" +#include "rive/viewmodel/viewmodel_property.hpp" +#include "rive/viewmodel/viewmodel_instance.hpp" +#include +namespace rive +{ +class ViewModel : public ViewModelBase +{ +private: + std::vector m_Properties; + std::vector m_Instances; + +public: + void addProperty(ViewModelProperty* property); + ViewModelProperty* property(const std::string& name); + ViewModelProperty* property(size_t index); + void addInstance(ViewModelInstance* value); + ViewModelInstance* instance(size_t index); + ViewModelInstance* instance(const std::string& name); + ViewModelInstance* defaultInstance(); +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_component.hpp b/include/rive/viewmodel/viewmodel_component.hpp new file mode 100644 index 00000000..28354b40 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_component.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_VIEW_MODEL_COMPONENT_HPP_ +#define _RIVE_VIEW_MODEL_COMPONENT_HPP_ +#include "rive/generated/viewmodel/viewmodel_component_base.hpp" +#include +namespace rive +{ +class ViewModelComponent : public ViewModelComponentBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_instance.hpp b/include/rive/viewmodel/viewmodel_instance.hpp new file mode 100644 index 00000000..3a6375a9 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_instance.hpp @@ -0,0 +1,32 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_HPP_ +#include "rive/generated/viewmodel/viewmodel_instance_base.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" +#include "rive/component.hpp" +#include +namespace rive +{ +class ViewModel; +class ViewModelInstance : public ViewModelInstanceBase +{ +private: + std::vector m_PropertyValues; + ViewModel* m_ViewModel; + +public: + void addValue(ViewModelInstanceValue* value); + ViewModelInstanceValue* propertyValue(const uint32_t id); + ViewModelInstanceValue* propertyValue(const std::string& name); + std::vector propertyValues(); + ViewModelInstanceValue* propertyFromPath(std::vector* path, size_t index); + void viewModel(ViewModel* value); + ViewModel* viewModel() const; + void onComponentDirty(Component* component); + void setAsRoot(); + void setRoot(ViewModelInstance* value); + Core* clone() const override; + StatusCode import(ImportStack& importStack) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_instance_boolean.hpp b/include/rive/viewmodel/viewmodel_instance_boolean.hpp new file mode 100644 index 00000000..c00c159b --- /dev/null +++ b/include/rive/viewmodel/viewmodel_instance_boolean.hpp @@ -0,0 +1,14 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_BOOLEAN_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_BOOLEAN_HPP_ +#include "rive/generated/viewmodel/viewmodel_instance_boolean_base.hpp" +#include +namespace rive +{ +class ViewModelInstanceBoolean : public ViewModelInstanceBooleanBase +{ +protected: + void propertyValueChanged() override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_instance_color.hpp b/include/rive/viewmodel/viewmodel_instance_color.hpp new file mode 100644 index 00000000..833ddbb4 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_instance_color.hpp @@ -0,0 +1,14 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_COLOR_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_COLOR_HPP_ +#include "rive/generated/viewmodel/viewmodel_instance_color_base.hpp" +#include +namespace rive +{ +class ViewModelInstanceColor : public ViewModelInstanceColorBase +{ +public: + void propertyValueChanged() override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_instance_enum.hpp b/include/rive/viewmodel/viewmodel_instance_enum.hpp new file mode 100644 index 00000000..6980ae45 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_instance_enum.hpp @@ -0,0 +1,18 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_ENUM_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_ENUM_HPP_ +#include "rive/generated/viewmodel/viewmodel_instance_enum_base.hpp" +#include +namespace rive +{ +class ViewModelInstanceEnum : public ViewModelInstanceEnumBase +{ +public: + bool value(std::string name); + bool value(uint32_t index); + +protected: + void propertyValueChanged() override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_instance_list.hpp b/include/rive/viewmodel/viewmodel_instance_list.hpp new file mode 100644 index 00000000..8f1b9348 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_instance_list.hpp @@ -0,0 +1,26 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_LIST_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_LIST_HPP_ +#include "rive/generated/viewmodel/viewmodel_instance_list_base.hpp" +#include "rive/viewmodel/viewmodel_instance_list_item.hpp" +#include +namespace rive +{ +class ViewModelInstanceList : public ViewModelInstanceListBase +{ +public: + void addItem(ViewModelInstanceListItem* listItem); + void insertItem(int index, ViewModelInstanceListItem* listItem); + void removeItem(int index); + void removeItem(ViewModelInstanceListItem* listItem); + std::vector listItems() { return m_ListItems; }; + ViewModelInstanceListItem* item(uint32_t index); + void swap(uint32_t index1, uint32_t index2); + Core* clone() const override; + +protected: + std::vector m_ListItems; + void propertyValueChanged(); +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_instance_list_item.hpp b/include/rive/viewmodel/viewmodel_instance_list_item.hpp new file mode 100644 index 00000000..d09ab721 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_instance_list_item.hpp @@ -0,0 +1,23 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_LIST_ITEM_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_LIST_ITEM_HPP_ +#include "rive/generated/viewmodel/viewmodel_instance_list_item_base.hpp" +#include "rive/viewmodel/viewmodel_instance.hpp" +#include +namespace rive +{ +class ViewModelInstanceListItem : public ViewModelInstanceListItemBase +{ +private: + ViewModelInstance* m_viewModelInstance; + Artboard* m_artboard; + +public: + void viewModelInstance(ViewModelInstance* value) { m_viewModelInstance = value; }; + ViewModelInstance* viewModelInstance() { return m_viewModelInstance; } + void artboard(Artboard* value) { m_artboard = value; }; + Artboard* artboard() { return m_artboard; } + StatusCode import(ImportStack& importStack) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_instance_number.hpp b/include/rive/viewmodel/viewmodel_instance_number.hpp new file mode 100644 index 00000000..a5f40e93 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_instance_number.hpp @@ -0,0 +1,14 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_NUMBER_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_NUMBER_HPP_ +#include "rive/generated/viewmodel/viewmodel_instance_number_base.hpp" +#include +namespace rive +{ +class ViewModelInstanceNumber : public ViewModelInstanceNumberBase +{ +protected: + void propertyValueChanged() override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_instance_string.hpp b/include/rive/viewmodel/viewmodel_instance_string.hpp new file mode 100644 index 00000000..a7234e84 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_instance_string.hpp @@ -0,0 +1,14 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_STRING_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_STRING_HPP_ +#include "rive/generated/viewmodel/viewmodel_instance_string_base.hpp" +#include +namespace rive +{ +class ViewModelInstanceString : public ViewModelInstanceStringBase +{ +public: + void propertyValueChanged() override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_instance_value.hpp b/include/rive/viewmodel/viewmodel_instance_value.hpp new file mode 100644 index 00000000..484aa8e3 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_instance_value.hpp @@ -0,0 +1,31 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_VALUE_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_VALUE_HPP_ +#include "rive/generated/viewmodel/viewmodel_instance_value_base.hpp" +#include "rive/viewmodel/viewmodel_property.hpp" +#include "rive/dependency_helper.hpp" +#include "rive/component.hpp" +#include "rive/component_dirt.hpp" +#include +namespace rive +{ +class DataBind; +class ViewModelInstance; +class ViewModelInstanceValue : public ViewModelInstanceValueBase +{ +private: + ViewModelProperty* m_ViewModelProperty; + +protected: + DependencyHelper m_DependencyHelper; + void addDirt(ComponentDirt value); + +public: + StatusCode import(ImportStack& importStack) override; + void viewModelProperty(ViewModelProperty* value); + ViewModelProperty* viewModelProperty(); + void addDependent(DataBind* value); + virtual void setRoot(ViewModelInstance* value); +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_instance_viewmodel.hpp b/include/rive/viewmodel/viewmodel_instance_viewmodel.hpp new file mode 100644 index 00000000..d2ea9ab4 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_instance_viewmodel.hpp @@ -0,0 +1,23 @@ +#ifndef _RIVE_VIEW_MODEL_INSTANCE_VIEW_MODEL_HPP_ +#define _RIVE_VIEW_MODEL_INSTANCE_VIEW_MODEL_HPP_ +#include "rive/generated/viewmodel/viewmodel_instance_viewmodel_base.hpp" +#include "rive/viewmodel/viewmodel_instance.hpp" +#include +namespace rive +{ +class ViewModelInstanceViewModel : public ViewModelInstanceViewModelBase +{ +private: + ViewModelInstance* m_referenceViewModelInstance; + +public: + void referenceViewModelInstance(ViewModelInstance* value) + { + m_referenceViewModelInstance = value; + }; + ViewModelInstance* referenceViewModelInstance() { return m_referenceViewModelInstance; } + void setRoot(ViewModelInstance* value) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_property.hpp b/include/rive/viewmodel/viewmodel_property.hpp new file mode 100644 index 00000000..87b3a25f --- /dev/null +++ b/include/rive/viewmodel/viewmodel_property.hpp @@ -0,0 +1,14 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_HPP_ +#include "rive/generated/viewmodel/viewmodel_property_base.hpp" +#include +namespace rive +{ +class ViewModelProperty : public ViewModelPropertyBase +{ +public: + StatusCode import(ImportStack& importStack) override; +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_property_boolean.hpp b/include/rive/viewmodel/viewmodel_property_boolean.hpp new file mode 100644 index 00000000..c86fb615 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_property_boolean.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_BOOLEAN_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_BOOLEAN_HPP_ +#include "rive/generated/viewmodel/viewmodel_property_boolean_base.hpp" +#include +namespace rive +{ +class ViewModelPropertyBoolean : public ViewModelPropertyBooleanBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_property_color.hpp b/include/rive/viewmodel/viewmodel_property_color.hpp new file mode 100644 index 00000000..ffc1191a --- /dev/null +++ b/include/rive/viewmodel/viewmodel_property_color.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_COLOR_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_COLOR_HPP_ +#include "rive/generated/viewmodel/viewmodel_property_color_base.hpp" +#include +namespace rive +{ +class ViewModelPropertyColor : public ViewModelPropertyColorBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_property_enum.hpp b/include/rive/viewmodel/viewmodel_property_enum.hpp new file mode 100644 index 00000000..41582e43 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_property_enum.hpp @@ -0,0 +1,27 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_ENUM_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_ENUM_HPP_ +#include "rive/generated/viewmodel/viewmodel_property_enum_base.hpp" +#include "rive/viewmodel/data_enum.hpp" +#include +namespace rive +{ +class ViewModelPropertyEnum : public ViewModelPropertyEnumBase +{ + +public: + std::string value(std::string name); + std::string value(uint32_t index); + bool value(std::string name, std::string value); + bool value(uint32_t index, std::string value); + int valueIndex(std::string name); + int valueIndex(uint32_t index); + void dataEnum(DataEnum* value); + DataEnum* dataEnum(); + +private: + DataEnum* m_DataEnum; +}; + +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_property_list.hpp b/include/rive/viewmodel/viewmodel_property_list.hpp new file mode 100644 index 00000000..a4fa054d --- /dev/null +++ b/include/rive/viewmodel/viewmodel_property_list.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_LIST_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_LIST_HPP_ +#include "rive/generated/viewmodel/viewmodel_property_list_base.hpp" +#include +namespace rive +{ +class ViewModelPropertyList : public ViewModelPropertyListBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_property_number.hpp b/include/rive/viewmodel/viewmodel_property_number.hpp new file mode 100644 index 00000000..0ba24683 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_property_number.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_NUMBER_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_NUMBER_HPP_ +#include "rive/generated/viewmodel/viewmodel_property_number_base.hpp" +#include +namespace rive +{ +class ViewModelPropertyNumber : public ViewModelPropertyNumberBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_property_string.hpp b/include/rive/viewmodel/viewmodel_property_string.hpp new file mode 100644 index 00000000..0ff37458 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_property_string.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_STRING_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_STRING_HPP_ +#include "rive/generated/viewmodel/viewmodel_property_string_base.hpp" +#include +namespace rive +{ +class ViewModelPropertyString : public ViewModelPropertyStringBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/include/rive/viewmodel/viewmodel_property_viewmodel.hpp b/include/rive/viewmodel/viewmodel_property_viewmodel.hpp new file mode 100644 index 00000000..32ae2133 --- /dev/null +++ b/include/rive/viewmodel/viewmodel_property_viewmodel.hpp @@ -0,0 +1,13 @@ +#ifndef _RIVE_VIEW_MODEL_PROPERTY_VIEW_MODEL_HPP_ +#define _RIVE_VIEW_MODEL_PROPERTY_VIEW_MODEL_HPP_ +#include "rive/generated/viewmodel/viewmodel_property_viewmodel_base.hpp" +#include +namespace rive +{ +class ViewModelPropertyViewModel : public ViewModelPropertyViewModelBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file diff --git a/premake5_v2.lua b/premake5_v2.lua index 0787eddc..35acb878 100644 --- a/premake5_v2.lua +++ b/premake5_v2.lua @@ -8,14 +8,23 @@ filter({ 'options:with_rive_text' }) do defines({ 'WITH_RIVE_TEXT' }) end -filter({}) filter({ 'options:with_rive_audio=system' }) do - defines({ 'WITH_RIVE_AUDIO' }) + defines({ 'WITH_RIVE_AUDIO', 'MA_NO_RESOURCE_MANAGER' }) end + filter({ 'options:with_rive_audio=external' }) do - defines({ 'WITH_RIVE_AUDIO', 'EXTERNAL_RIVE_AUDIO_ENGINE', 'MA_NO_DEVICE_IO' }) + defines({ + 'WITH_RIVE_AUDIO', + 'EXTERNAL_RIVE_AUDIO_ENGINE', + 'MA_NO_DEVICE_IO', + 'MA_NO_RESOURCE_MANAGER', + }) +end +filter({ 'options:with_rive_layout' }) +do + defines({ 'WITH_RIVE_LAYOUT' }) end filter({}) @@ -23,6 +32,7 @@ dependencies = path.getabsolute('dependencies/') dofile(path.join(dependencies, 'premake5_harfbuzz_v2.lua')) dofile(path.join(dependencies, 'premake5_sheenbidi_v2.lua')) dofile(path.join(dependencies, 'premake5_miniaudio_v2.lua')) +dofile(path.join(dependencies, 'premake5_yoga_v2.lua')) project('rive') do @@ -33,8 +43,19 @@ do harfbuzz .. '/src', sheenbidi .. '/Headers', miniaudio, + yoga, }) + filter('action:xcode4') + do + -- xcode doesnt like angle brackets except for -isystem + -- should use externalincludedirs but GitHub runners dont have latest premake5 binaries + buildoptions({ '-isystem' .. yoga }) + end + filter({}) + + defines({ 'YOGA_EXPORT=' }) + files({ 'src/**.cpp' }) flags({ 'FatalCompileWarnings' }) @@ -47,6 +68,14 @@ do forceincludes({ 'rive_harfbuzz_renames.h' }) end + filter({ 'options:not no-yoga-renames' }) + do + includedirs({ + dependencies, + }) + forceincludes({ 'rive_yoga_renames.h' }) + end + filter({ 'system:linux' }) do defines({ 'MA_NO_RUNTIME_LINKING' }) @@ -89,6 +118,7 @@ do '-Wno-implicit-fallthrough', '-Wno-implicit-int-conversion', '-Wno-undef', + '-Wno-unused-function', }) end @@ -118,6 +148,11 @@ do architecture('x64') defines({ '_USE_MATH_DEFINES' }) end + + filter('system:macosx or system:ios') + do + files({ 'src/text/font_hb_apple.mm' }) + end end newoption({ @@ -151,3 +186,8 @@ newoption({ description = 'The audio mode to use.', allowed = { { 'disabled' }, { 'system' }, { 'external' } }, }) + +newoption({ + trigger = 'with_rive_layout', + description = 'Compiles in layout features.', +}) diff --git a/skia/renderer/include/to_skia.hpp b/skia/renderer/include/to_skia.hpp index cc49d249..0cf8920d 100644 --- a/skia/renderer/include/to_skia.hpp +++ b/skia/renderer/include/to_skia.hpp @@ -12,6 +12,7 @@ #include "include/core/SkPathTypes.h" #include "include/core/SkTileMode.h" +#include "rive/math/math_types.hpp" #include "rive/math/mat2d.hpp" #include "rive/math/raw_path.hpp" #include "rive/math/vec2d.hpp" @@ -89,7 +90,7 @@ class ToSkia const auto pts = rp.points(); const auto vbs = rp.verbsU8(); return SkPath::Make((const SkPoint*)pts.data(), pts.size(), - vbs.data(), vbs.size(), + vbs.data(), math::lossless_numeric_cast(vbs.size()), nullptr, 0, SkPathFillType::kWinding); } // clang-format off diff --git a/skia/thumbnail_generator/build.sh b/skia/thumbnail_generator/build.sh index 3fb31108..34de3fd1 100755 --- a/skia/thumbnail_generator/build.sh +++ b/skia/thumbnail_generator/build.sh @@ -26,7 +26,7 @@ elif [ "$OPTION" = "clean" ]; then echo Cleaning project ... premake5 clean --scripts="$RIVE_RUNTIME_DIR/build" elif [ "$OPTION" = "release" ]; then - premake5 gmake --scripts="$RIVE_RUNTIME_DIR/build" --with_rive_text && make config=release -j7 + premake5 gmake --scripts="$RIVE_RUNTIME_DIR/build" --with_rive_text --with_rive_layout && make config=release -j7 else - premake5 gmake --scripts="$RIVE_RUNTIME_DIR/build" --with_rive_text && make -j7 + premake5 gmake --scripts="$RIVE_RUNTIME_DIR/build" --with_rive_text --with_rive_layout && make -j7 fi diff --git a/skia/thumbnail_generator/build/premake5.lua b/skia/thumbnail_generator/build/premake5.lua index 66fb3d7c..a724d647 100644 --- a/skia/thumbnail_generator/build/premake5.lua +++ b/skia/thumbnail_generator/build/premake5.lua @@ -70,6 +70,15 @@ defines({ 'RELEASE' }) defines({ 'NDEBUG' }) optimize('On') +filter({ 'options:with_rive_layout' }) +do + defines({ 'YOGA_EXPORT=' }) + includedirs({ yoga }) + links({ + 'rive_yoga', + }) +end + -- Clean Function -- newaction({ trigger = 'clean', diff --git a/src/animation/animation_reset.cpp b/src/animation/animation_reset.cpp new file mode 100644 index 00000000..2faf0d50 --- /dev/null +++ b/src/animation/animation_reset.cpp @@ -0,0 +1,49 @@ +#include "rive/animation/animation_reset.hpp" +#include "rive/core/vector_binary_writer.hpp" +#include "rive/generated/core_registry.hpp" + +using namespace rive; + +AnimationReset::AnimationReset() : m_binaryWriter(&m_WriteBuffer), m_binaryReader(nullptr, 0) {} + +void AnimationReset::writeObjectId(uint32_t objectId) { m_binaryWriter.writeVarUint(objectId); } + +void AnimationReset::writeTotalProperties(uint32_t value) { m_binaryWriter.writeVarUint(value); } + +void AnimationReset::writePropertyKey(uint32_t value) { m_binaryWriter.writeVarUint(value); } + +void AnimationReset::writePropertyValue(float value) { m_binaryWriter.writeFloat(value); } + +void AnimationReset::clear() { m_binaryWriter.clear(); } + +void AnimationReset::complete() +{ + m_binaryReader.complete(&m_WriteBuffer.front(), m_binaryWriter.size()); +} + +void AnimationReset::apply(Artboard* artboard) +{ + m_binaryReader.reset(&m_WriteBuffer.front()); + while (!m_binaryReader.isEOF()) + { + auto objectId = m_binaryReader.readVarUint32(); + auto object = artboard->resolve(objectId); + auto totalProperties = m_binaryReader.readVarUint32(); + uint32_t currentPropertyIndex = 0; + while (currentPropertyIndex < totalProperties) + { + auto propertyKey = m_binaryReader.readVarUint32(); + auto propertyValue = m_binaryReader.readFloat32(); + switch (CoreRegistry::propertyFieldId(propertyKey)) + { + case CoreDoubleType::id: + CoreRegistry::setDouble(object, propertyKey, propertyValue); + break; + case CoreColorType::id: + CoreRegistry::setColor(object, propertyKey, propertyValue); + break; + } + currentPropertyIndex++; + } + } +} diff --git a/src/animation/animation_reset_factory.cpp b/src/animation/animation_reset_factory.cpp new file mode 100644 index 00000000..55ca9640 --- /dev/null +++ b/src/animation/animation_reset_factory.cpp @@ -0,0 +1,215 @@ +#include "rive/animation/animation_reset_factory.hpp" +#include "rive/animation/linear_animation.hpp" +#include "rive/animation/animation_state.hpp" +#include "rive/animation/keyed_object.hpp" +#include "rive/animation/keyed_property.hpp" +#include "rive/generated/core_registry.hpp" +#include +#include + +using namespace rive; + +class KeyedPropertyData +{ +public: + const KeyedProperty* keyedProperty; + bool isBaseline; + KeyedPropertyData(const KeyedProperty* value, bool baselineValue) : + keyedProperty(value), isBaseline(baselineValue) + {} +}; + +class KeyedObjectData +{ +public: + std::vector keyedPropertiesData; + std::set keyedPropertiesSet; + uint32_t objectId; + KeyedObjectData(const uint32_t value) { objectId = value; } + void addProperties(const KeyedObject* keyedObject, bool isBaseline) + { + size_t index = 0; + while (index < keyedObject->numKeyedProperties()) + { + auto keyedProperty = keyedObject->getProperty(index); + auto pos = keyedPropertiesSet.find(keyedProperty->propertyKey()); + if (pos == keyedPropertiesSet.end()) + { + switch (CoreRegistry::propertyFieldId(keyedProperty->propertyKey())) + { + case CoreDoubleType::id: + case CoreColorType::id: + keyedPropertiesSet.insert(keyedProperty->propertyKey()); + keyedPropertiesData.push_back(KeyedPropertyData(keyedProperty, isBaseline)); + break; + } + } + index++; + } + } +}; + +class AnimationsData +{ + +private: + std::vector> keyedObjectsData; + KeyedObjectData* getKeyedObjectData(const KeyedObject* keyedObject) + { + for (auto& keyedObjectData : keyedObjectsData) + { + if (keyedObjectData->objectId == keyedObject->objectId()) + { + return keyedObjectData.get(); + } + } + + auto keyedObjectData = rivestd::make_unique(keyedObject->objectId()); + auto ref = keyedObjectData.get(); + keyedObjectsData.push_back(std::move(keyedObjectData)); + return ref; + } + + void findKeyedObjects(const LinearAnimation* animation, bool isFirstAnimation) + { + size_t index = 0; + while (index < animation->numKeyedObjects()) + { + auto keyedObject = animation->getObject(index); + auto keyedObjectData = getKeyedObjectData(keyedObject); + + keyedObjectData->addProperties(keyedObject, isFirstAnimation); + index++; + } + } + +public: + AnimationsData(std::vector& animations, bool useFirstAsBaseline) + { + bool isFirstAnimation = useFirstAsBaseline; + for (auto animation : animations) + { + findKeyedObjects(animation, isFirstAnimation); + isFirstAnimation = false; + } + } + + void writeObjects(AnimationReset* animationReset, ArtboardInstance* artboard) + { + for (auto& keyedObjectData : keyedObjectsData) + { + auto object = artboard->resolve(keyedObjectData->objectId)->as(); + auto propertiesData = keyedObjectData->keyedPropertiesData; + if (propertiesData.size() > 0) + { + animationReset->writeObjectId(keyedObjectData->objectId); + animationReset->writeTotalProperties(propertiesData.size()); + for (auto keyedPropertyData : propertiesData) + { + auto keyedProperty = keyedPropertyData.keyedProperty; + auto propertyKey = keyedProperty->propertyKey(); + switch (CoreRegistry::propertyFieldId(propertyKey)) + { + case CoreDoubleType::id: + animationReset->writePropertyKey(propertyKey); + if (keyedPropertyData.isBaseline) + { + auto firstKeyframe = keyedProperty->first(); + if (firstKeyframe != nullptr) + { + auto value = + keyedProperty->first()->as()->value(); + animationReset->writePropertyValue(value); + } + } + else + { + animationReset->writePropertyValue( + CoreRegistry::getDouble(object, propertyKey)); + } + break; + case CoreColorType::id: + + animationReset->writePropertyKey(propertyKey); + if (keyedPropertyData.isBaseline) + { + auto firstKeyframe = keyedProperty->first(); + if (firstKeyframe != nullptr) + { + auto value = + keyedProperty->first()->as()->value(); + animationReset->writePropertyValue(value); + } + } + else + { + animationReset->writePropertyValue( + CoreRegistry::getColor(object, propertyKey)); + } + break; + } + } + } + } + animationReset->complete(); + } +}; + +std::unique_ptr AnimationResetFactory::getInstance() +{ + std::unique_lock lock(m_mutex); + if (m_resources.size() > 0) + { + auto instance = std::move(m_resources.back()); + m_resources.pop_back(); + return instance; + } + auto instance = rivestd::make_unique(); + return instance; +} + +void AnimationResetFactory::fromState(StateInstance* stateInstance, + std::vector& animations) +{ + if (stateInstance != nullptr) + { + auto state = stateInstance->state(); + if (state->is() && state->as()->animation() != nullptr) + { + animations.push_back(state->as()->animation()); + } + } +} + +std::unique_ptr AnimationResetFactory::fromStates(StateInstance* stateFrom, + StateInstance* currentState, + ArtboardInstance* artboard) +{ + std::vector animations; + fromState(stateFrom, animations); + fromState(currentState, animations); + return fromAnimations(animations, artboard, false); +} + +std::unique_ptr AnimationResetFactory::fromAnimations( + std::vector& animations, + ArtboardInstance* artboard, + bool useFirstAsBaseline) +{ + auto animationsData = new AnimationsData(animations, useFirstAsBaseline); + auto animationReset = AnimationResetFactory::getInstance(); + animationsData->writeObjects(animationReset.get(), artboard); + delete animationsData; + return animationReset; +} + +std::vector> AnimationResetFactory::m_resources; + +std::mutex AnimationResetFactory::m_mutex; + +void AnimationResetFactory::release(std::unique_ptr value) +{ + std::unique_lock lock(m_mutex); + value->clear(); + m_resources.push_back(std::move(value)); +} diff --git a/src/animation/animation_state_instance.cpp b/src/animation/animation_state_instance.cpp index 07395500..5cae3e8f 100644 --- a/src/animation/animation_state_instance.cpp +++ b/src/animation/animation_state_instance.cpp @@ -30,7 +30,10 @@ void AnimationStateInstance::advance(float seconds, StateMachineInstance* stateM stateMachineInstance); } -void AnimationStateInstance::apply(float mix) { m_AnimationInstance.apply(mix); } +void AnimationStateInstance::apply(ArtboardInstance* instance, float mix) +{ + m_AnimationInstance.apply(mix); +} bool AnimationStateInstance::keepGoing() const { return m_KeepGoing; } void AnimationStateInstance::clearSpilledTime() { m_AnimationInstance.clearSpilledTime(); } \ No newline at end of file diff --git a/src/animation/blend_state_1d_instance.cpp b/src/animation/blend_state_1d_instance.cpp index 69022737..f1049ba0 100644 --- a/src/animation/blend_state_1d_instance.cpp +++ b/src/animation/blend_state_1d_instance.cpp @@ -1,12 +1,33 @@ #include "rive/animation/blend_state_1d_instance.hpp" #include "rive/animation/state_machine_input_instance.hpp" +#include "rive/animation/layer_state_flags.hpp" using namespace rive; BlendState1DInstance::BlendState1DInstance(const BlendState1D* blendState, ArtboardInstance* instance) : BlendStateInstance(blendState, instance) -{} +{ + + if ((static_cast(blendState->flags()) & LayerStateFlags::Reset) == + LayerStateFlags::Reset) + { + auto animations = std::vector(); + for (auto blendAnimation : blendState->animations()) + { + animations.push_back(blendAnimation->animation()); + } + m_AnimationReset = AnimationResetFactory::fromAnimations(animations, instance, true); + } +} + +BlendState1DInstance::~BlendState1DInstance() +{ + if (m_AnimationReset != nullptr) + { + AnimationResetFactory::release(std::move(m_AnimationReset)); + } +} int BlendState1DInstance::animationIndex(float value) { @@ -39,6 +60,15 @@ int BlendState1DInstance::animationIndex(float value) return idx; } +void BlendState1DInstance::apply(ArtboardInstance* instance, float mix) +{ + if (m_AnimationReset != nullptr) + { + m_AnimationReset->apply(instance); + } + BlendStateInstance::apply(instance, mix); +} + void BlendState1DInstance::advance(float seconds, StateMachineInstance* stateMachineInstance) { BlendStateInstance::advance(seconds, stateMachineInstance); diff --git a/src/animation/keyed_object.cpp b/src/animation/keyed_object.cpp index 3666199f..0bb8e63a 100644 --- a/src/animation/keyed_object.cpp +++ b/src/animation/keyed_object.cpp @@ -18,13 +18,19 @@ void KeyedObject::addKeyedProperty(std::unique_ptr property) StatusCode KeyedObject::onAddedDirty(CoreContext* context) { // Make sure we're keying a valid object. - if (context->resolve(objectId()) == nullptr) + Core* coreObject = context->resolve(objectId()); + if (coreObject == nullptr) { return StatusCode::MissingObject; } for (auto& property : m_keyedProperties) { + // Validate coreObject supports propertyKey + if (!CoreRegistry::objectSupportsProperty(coreObject, property->propertyKey())) + { + return StatusCode::InvalidObject; + } StatusCode code; if ((code = property->onAddedDirty(context)) != StatusCode::Ok) { @@ -46,7 +52,7 @@ StatusCode KeyedObject::onAddedClean(CoreContext* context) void KeyedObject::reportKeyedCallbacks(KeyedCallbackReporter* reporter, float secondsFrom, float secondsTo, - int secondsFromExactOffset) const + bool isAtStartFrame) const { for (const std::unique_ptr& property : m_keyedProperties) { @@ -58,7 +64,7 @@ void KeyedObject::reportKeyedCallbacks(KeyedCallbackReporter* reporter, objectId(), secondsFrom, secondsTo, - secondsFromExactOffset); + isAtStartFrame); } } diff --git a/src/animation/keyed_property.cpp b/src/animation/keyed_property.cpp index 750e976d..7156fb89 100644 --- a/src/animation/keyed_property.cpp +++ b/src/animation/keyed_property.cpp @@ -18,13 +18,18 @@ void KeyedProperty::addKeyFrame(std::unique_ptr keyframe) int KeyedProperty::closestFrameIndex(float seconds, int exactOffset) const { - int idx = 0; int mid = 0; float closestSeconds = 0; int start = 0; auto numKeyFrames = static_cast(m_keyFrames.size()); int end = numKeyFrames - 1; + // If it's the last keyframe, we skip the binary search + if (seconds > m_keyFrames[end]->seconds()) + { + return end + 1; + } + while (start <= end) { mid = (start + end) >> 1; @@ -41,19 +46,39 @@ int KeyedProperty::closestFrameIndex(float seconds, int exactOffset) const { return mid + exactOffset; } - idx = start; } - return idx; + return start; } void KeyedProperty::reportKeyedCallbacks(KeyedCallbackReporter* reporter, uint32_t objectId, float secondsFrom, float secondsTo, - int secondsFromExactOffset) const + bool isAtStartFrame) const { - int idx = closestFrameIndex(secondsFrom, secondsFromExactOffset); - int idxTo = closestFrameIndex(secondsTo, 1); + if (secondsFrom == secondsTo) + { + return; + } + bool isForward = secondsFrom <= secondsTo; + int fromExactOffset = 0; + int toExactOffset = isForward ? 1 : 0; + if (isForward) + { + if (!isAtStartFrame) + { + fromExactOffset = 1; + } + } + else + { + if (isAtStartFrame) + { + fromExactOffset = 1; + } + } + int idx = closestFrameIndex(secondsFrom, fromExactOffset); + int idxTo = closestFrameIndex(secondsTo, toExactOffset); if (idxTo < idx) { diff --git a/src/animation/keyframe_uint.cpp b/src/animation/keyframe_uint.cpp new file mode 100644 index 00000000..398abe3a --- /dev/null +++ b/src/animation/keyframe_uint.cpp @@ -0,0 +1,18 @@ +#include "rive/animation/keyframe_uint.hpp" +#include "rive/generated/core_registry.hpp" + +using namespace rive; + +void KeyFrameUint::apply(Core* object, int propertyKey, float mix) +{ + CoreRegistry::setUint(object, propertyKey, value()); +} + +void KeyFrameUint::applyInterpolation(Core* object, + int propertyKey, + float currentTime, + const KeyFrame* nextFrame, + float mix) +{ + CoreRegistry::setUint(object, propertyKey, value()); +} \ No newline at end of file diff --git a/src/animation/linear_animation.cpp b/src/animation/linear_animation.cpp index c2ade3b5..21d795d9 100644 --- a/src/animation/linear_animation.cpp +++ b/src/animation/linear_animation.cpp @@ -86,6 +86,10 @@ float LinearAnimation::endSeconds() const } float LinearAnimation::startTime() const { return (speed() >= 0) ? startSeconds() : endSeconds(); } +float LinearAnimation::startTime(float multiplier) const +{ + return ((speed() * multiplier) >= 0) ? startSeconds() : endSeconds(); +} float LinearAnimation::endTime() const { return (speed() >= 0) ? endSeconds() : startSeconds(); } float LinearAnimation::durationSeconds() const { return std::abs(endSeconds() - startSeconds()); } @@ -119,16 +123,18 @@ float LinearAnimation::globalToLocalSeconds(float seconds) const void LinearAnimation::reportKeyedCallbacks(KeyedCallbackReporter* reporter, float secondsFrom, - float secondsTo) const + float secondsTo, + float speedDirection, + bool fromPong) const { - int secondsFromExactOffset = - startTime() == secondsFrom && - (speed() >= 0 ? secondsFrom < secondsTo : secondsFrom < secondsTo) - ? 0 - : 1; + float startingTime = startTime(speedDirection); + bool isAtStartFrame = startingTime == secondsFrom; - for (const auto& object : m_KeyedObjects) + if (!isAtStartFrame || !fromPong) { - object->reportKeyedCallbacks(reporter, secondsFrom, secondsTo, secondsFromExactOffset); + for (const auto& object : m_KeyedObjects) + { + object->reportKeyedCallbacks(reporter, secondsFrom, secondsTo, isAtStartFrame); + } } } \ No newline at end of file diff --git a/src/animation/linear_animation_instance.cpp b/src/animation/linear_animation_instance.cpp index 7e132d46..08ebd16b 100644 --- a/src/animation/linear_animation_instance.cpp +++ b/src/animation/linear_animation_instance.cpp @@ -13,6 +13,7 @@ LinearAnimationInstance::LinearAnimationInstance(const LinearAnimation* animatio Scene(instance), m_animation((assert(animation != nullptr), animation)), m_time((speedMultiplier >= 0) ? animation->startTime() : animation->endTime()), + m_speedDirection((speedMultiplier >= 0) ? 1 : -1), m_totalTime(0.0f), m_lastTotalTime(0.0f), m_spilledTime(0.0f), @@ -23,6 +24,7 @@ LinearAnimationInstance::LinearAnimationInstance(LinearAnimationInstance const& Scene(lhs), m_animation(lhs.m_animation), m_time(lhs.m_time), + m_speedDirection(lhs.m_speedDirection), m_totalTime(lhs.m_totalTime), m_lastTotalTime(lhs.m_lastTotalTime), m_spilledTime(lhs.m_spilledTime), @@ -58,13 +60,13 @@ bool LinearAnimationInstance::advance(float elapsedSeconds, KeyedCallbackReporte // NOTE: // do not track spilled time, if our one shot loop is already completed. // stop gap before we move spilled tracking into state machine logic. - bool killSpilledTime = !this->keepGoing(); + bool killSpilledTime = !this->keepGoing(elapsedSeconds); float lastTime = m_time; m_time += deltaSeconds; if (reporter != nullptr) { - animation.reportKeyedCallbacks(reporter, lastTime, m_time); + animation.reportKeyedCallbacks(reporter, lastTime, m_time, m_speedDirection, false); } int fps = animation.fps(); @@ -84,14 +86,23 @@ bool LinearAnimationInstance::advance(float elapsedSeconds, KeyedCallbackReporte case Loop::oneShot: if (direction == 1 && frames > end) { - m_spilledTime = (frames - end) / fps; + // Account for the time dilation or contraction applied in the + // animation local time by its speed to calculate spilled time. + // Calculate the ratio of the time excess by the total elapsed + // time in local time (deltaFrames) and multiply the elapsed time + // by it. + auto deltaFrames = deltaSeconds * fps; + auto spilledFramesRatio = (frames - end) / deltaFrames; + m_spilledTime = spilledFramesRatio * elapsedSeconds; frames = (float)end; m_time = frames / fps; didLoop = true; } else if (direction == -1 && frames < start) { - m_spilledTime = (start - frames) / fps; + auto deltaFrames = std::abs(deltaSeconds * fps); + auto spilledFramesRatio = (start - frames) / deltaFrames; + m_spilledTime = spilledFramesRatio * elapsedSeconds; frames = (float)start; m_time = frames / fps; didLoop = true; @@ -100,30 +111,46 @@ bool LinearAnimationInstance::advance(float elapsedSeconds, KeyedCallbackReporte case Loop::loop: if (direction == 1 && frames >= end) { - m_spilledTime = (frames - end) / fps; - frames = m_time * fps; - frames = start + std::fmod(frames - start, (float)range); + // How spilled time has to be calculated, given that local time can be scaled + // to a factor of the regular time: + // - for convenience, calculate the local elapsed time in frames (deltaFrames) + // - get the remainder of current frame position (frames) by duration (range) + // - use that remainder as the ratio of the original time that was not consumed + // by the loop (spilledFramesRatio) + // - multiply the original elapsedTime by the ratio to set the spilled time + auto deltaFrames = deltaSeconds * fps; + auto remainder = std::fmod(frames - start, (float)range); + auto spilledFramesRatio = remainder / deltaFrames; + m_spilledTime = spilledFramesRatio * elapsedSeconds; + frames = start + remainder; m_time = frames / fps; didLoop = true; if (reporter != nullptr) { - animation.reportKeyedCallbacks(reporter, 0.0f, m_time); + animation.reportKeyedCallbacks(reporter, 0.0f, m_time, m_speedDirection, false); } } else if (direction == -1 && frames <= start) { - m_spilledTime = (start - frames) / fps; - frames = m_time * fps; - frames = end - std::abs(std::fmod(start - frames, (float)range)); + auto deltaFrames = deltaSeconds * fps; + auto remainder = std::abs(std::fmod(start - frames, (float)range)); + auto spilledFramesRatio = std::abs(remainder / deltaFrames); + m_spilledTime = spilledFramesRatio * elapsedSeconds; + frames = end - remainder; m_time = frames / fps; didLoop = true; if (reporter != nullptr) { - animation.reportKeyedCallbacks(reporter, end / (float)fps, m_time); + animation.reportKeyedCallbacks(reporter, + end / (float)fps, + m_time, + m_speedDirection, + false); } } break; case Loop::pingPong: + bool fromPong = true; while (true) { if (direction == 1 && frames >= end) @@ -153,8 +180,13 @@ bool LinearAnimationInstance::advance(float elapsedSeconds, KeyedCallbackReporte didLoop = true; if (reporter != nullptr) { - animation.reportKeyedCallbacks(reporter, lastTime, m_time); + animation.reportKeyedCallbacks(reporter, + lastTime, + m_time, + m_speedDirection, + fromPong); } + fromPong = !fromPong; } break; } @@ -165,7 +197,7 @@ bool LinearAnimationInstance::advance(float elapsedSeconds, KeyedCallbackReporte } m_didLoop = didLoop; - return this->keepGoing(); + return this->keepGoing(elapsedSeconds); } void LinearAnimationInstance::time(float value) @@ -233,3 +265,9 @@ void LinearAnimationInstance::loopValue(int value) } float LinearAnimationInstance::durationSeconds() const { return m_animation->durationSeconds(); } + +void LinearAnimationInstance::reportEvent(Event* event, float secondsDelay) +{ + const std::vector events{event}; + notifyListeners(events); +} \ No newline at end of file diff --git a/src/animation/listener_align_target.cpp b/src/animation/listener_align_target.cpp index c0a573a5..415321e6 100644 --- a/src/animation/listener_align_target.cpp +++ b/src/animation/listener_align_target.cpp @@ -5,7 +5,9 @@ using namespace rive; -void ListenerAlignTarget::perform(StateMachineInstance* stateMachineInstance, Vec2D position) const +void ListenerAlignTarget::perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const { auto coreTarget = stateMachineInstance->artboard()->resolve(targetId()); if (coreTarget == nullptr || !coreTarget->is()) @@ -19,8 +21,18 @@ void ListenerAlignTarget::perform(StateMachineInstance* stateMachineInstance, Ve { return; } + if (preserveOffset()) + { - auto localPosition = inverse * position; - target->x(localPosition.x); - target->y(localPosition.y); + auto localPosition = inverse * position; + auto prevLocalPosition = inverse * previousPosition; + target->x(target->x() + localPosition.x - prevLocalPosition.x); + target->y(target->y() + localPosition.y - prevLocalPosition.y); + } + else + { + auto localPosition = inverse * position; + target->x(localPosition.x); + target->y(localPosition.y); + } } diff --git a/src/animation/listener_bool_change.cpp b/src/animation/listener_bool_change.cpp index b86e0f86..e84808c8 100644 --- a/src/animation/listener_bool_change.cpp +++ b/src/animation/listener_bool_change.cpp @@ -23,7 +23,9 @@ bool ListenerBoolChange::validateNestedInputType(const NestedInput* input) const return input == nullptr || input->is(); } -void ListenerBoolChange::perform(StateMachineInstance* stateMachineInstance, Vec2D position) const +void ListenerBoolChange::perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const { if (nestedInputId() != Core::emptyId) { diff --git a/src/animation/listener_fire_event.cpp b/src/animation/listener_fire_event.cpp index 41e8dc63..bd6d0744 100644 --- a/src/animation/listener_fire_event.cpp +++ b/src/animation/listener_fire_event.cpp @@ -4,7 +4,9 @@ using namespace rive; -void ListenerFireEvent::perform(StateMachineInstance* stateMachineInstance, Vec2D position) const +void ListenerFireEvent::perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const { auto coreEvent = stateMachineInstance->artboard()->resolve(eventId()); if (coreEvent == nullptr || !coreEvent->is()) diff --git a/src/animation/listener_number_change.cpp b/src/animation/listener_number_change.cpp index ca1d5c25..61cac0c9 100644 --- a/src/animation/listener_number_change.cpp +++ b/src/animation/listener_number_change.cpp @@ -24,7 +24,9 @@ bool ListenerNumberChange::validateNestedInputType(const NestedInput* input) con return input == nullptr || input->is(); } -void ListenerNumberChange::perform(StateMachineInstance* stateMachineInstance, Vec2D position) const +void ListenerNumberChange::perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const { if (nestedInputId() != Core::emptyId) { diff --git a/src/animation/listener_trigger_change.cpp b/src/animation/listener_trigger_change.cpp index a7b05794..2832ff81 100644 --- a/src/animation/listener_trigger_change.cpp +++ b/src/animation/listener_trigger_change.cpp @@ -26,7 +26,8 @@ bool ListenerTriggerChange::validateNestedInputType(const NestedInput* input) co } void ListenerTriggerChange::perform(StateMachineInstance* stateMachineInstance, - Vec2D position) const + Vec2D position, + Vec2D previousPosition) const { if (nestedInputId() != Core::emptyId) { diff --git a/src/animation/listener_viewmodel_change.cpp b/src/animation/listener_viewmodel_change.cpp new file mode 100644 index 00000000..5047ee53 --- /dev/null +++ b/src/animation/listener_viewmodel_change.cpp @@ -0,0 +1,37 @@ +#include "rive/animation/listener_viewmodel_change.hpp" +#include "rive/animation/state_machine_instance.hpp" +#include "rive/data_bind/bindable_property.hpp" +#include "rive/importers/bindable_property_importer.hpp" + +using namespace rive; + +StatusCode ListenerViewModelChange::import(ImportStack& importStack) +{ + + auto bindablePropertyImporter = + importStack.latest(BindablePropertyBase::typeKey); + if (bindablePropertyImporter == nullptr) + { + return StatusCode::MissingObject; + } + m_bindableProperty = bindablePropertyImporter->bindableProperty(); + + return Super::import(importStack); +} + +// Note: perform works the same way whether the value comes from a direct value assignment or from +// another view model. In the case of coming from another view model, the state machine instance +// method "updataDataBinds" will handle updating the value of the bound object. That's the benefit +// of binding the same bindable property with two data binding objects. +void ListenerViewModelChange::perform(StateMachineInstance* stateMachineInstance, + Vec2D position, + Vec2D previousPosition) const +{ + // Get the bindable property instance from the state machine instance context + auto bindableInstance = stateMachineInstance->bindablePropertyInstance(m_bindableProperty); + // Get the data bound object (that goes from target to source) from this bindable instance + auto dataBind = stateMachineInstance->bindableDataBind(bindableInstance); + // Apply the change that will assign the value of the bindable property to the view model + // property instance + dataBind->updateSourceBinding(); +} \ No newline at end of file diff --git a/src/animation/nested_bool.cpp b/src/animation/nested_bool.cpp index 29cd646d..e575022d 100644 --- a/src/animation/nested_bool.cpp +++ b/src/animation/nested_bool.cpp @@ -6,8 +6,8 @@ using namespace rive; class StateMachineInstance; -void NestedBool::nestedValueChanged() { this->applyValue(); } - +// Use the NestedBoolBase m_NestedValue on initialization but then it won't +// be used anymore and interface directly with the nested input value. void NestedBool::applyValue() { auto inputInstance = input(); @@ -16,7 +16,34 @@ void NestedBool::applyValue() auto boolInput = static_cast(inputInstance); if (boolInput != nullptr) { - boolInput->value(nestedValue()); + boolInput->value(NestedBoolBase::nestedValue()); + } + } +} + +void NestedBool::nestedValue(bool value) +{ + auto inputInstance = input(); + if (inputInstance != nullptr) + { + auto boolInput = static_cast(inputInstance); + if (boolInput != nullptr && boolInput->value() != value) + { + boolInput->value(value); + } + } +} + +bool NestedBool::nestedValue() const +{ + auto inputInstance = input(); + if (inputInstance != nullptr) + { + auto boolInput = static_cast(inputInstance); + if (boolInput != nullptr) + { + return boolInput->value(); } } + return false; } \ No newline at end of file diff --git a/src/animation/nested_number.cpp b/src/animation/nested_number.cpp index a81236fb..a3d88293 100644 --- a/src/animation/nested_number.cpp +++ b/src/animation/nested_number.cpp @@ -6,8 +6,8 @@ using namespace rive; class StateMachineInstance; -void NestedNumber::nestedValueChanged() { this->applyValue(); } - +// Use the NestedNumberBase m_NestedValue on initialization but then it won't +// be used anymore and interface directly with the nested input value. void NestedNumber::applyValue() { auto inputInstance = input(); @@ -16,7 +16,34 @@ void NestedNumber::applyValue() auto numInput = static_cast(inputInstance); if (numInput != nullptr) { - numInput->value(nestedValue()); + numInput->value(NestedNumberBase::nestedValue()); + } + } +} + +void NestedNumber::nestedValue(float value) +{ + auto inputInstance = input(); + if (inputInstance != nullptr) + { + auto numInput = static_cast(inputInstance); + if (numInput != nullptr && numInput->value() != value) + { + numInput->value(value); + } + } +} + +float NestedNumber::nestedValue() const +{ + auto inputInstance = input(); + if (inputInstance != nullptr) + { + auto numInput = static_cast(inputInstance); + if (numInput != nullptr) + { + return numInput->value(); } } + return 0.0; } \ No newline at end of file diff --git a/src/animation/nested_remap_animation.cpp b/src/animation/nested_remap_animation.cpp index 92b94653..4edc815f 100644 --- a/src/animation/nested_remap_animation.cpp +++ b/src/animation/nested_remap_animation.cpp @@ -18,10 +18,13 @@ void NestedRemapAnimation::initializeAnimation(ArtboardInstance* artboard) timeChanged(); } -void NestedRemapAnimation::advance(float elapsedSeconds) +bool NestedRemapAnimation::advance(float elapsedSeconds) { + bool keepGoing = false; if (m_AnimationInstance != nullptr && mix() != 0.0f) { m_AnimationInstance->apply(mix()); + keepGoing = true; } + return keepGoing; } \ No newline at end of file diff --git a/src/animation/nested_simple_animation.cpp b/src/animation/nested_simple_animation.cpp index 89ff29f8..c747fd29 100644 --- a/src/animation/nested_simple_animation.cpp +++ b/src/animation/nested_simple_animation.cpp @@ -3,17 +3,20 @@ using namespace rive; -void NestedSimpleAnimation::advance(float elapsedSeconds) +bool NestedSimpleAnimation::advance(float elapsedSeconds) { + bool keepGoing = false; if (m_AnimationInstance != nullptr) { if (isPlaying()) { - m_AnimationInstance->advance(elapsedSeconds * speed()); + keepGoing = + m_AnimationInstance->advance(elapsedSeconds * speed(), m_AnimationInstance.get()); } if (mix() != 0.0f) { m_AnimationInstance->apply(mix()); } } + return keepGoing; } \ No newline at end of file diff --git a/src/animation/nested_state_machine.cpp b/src/animation/nested_state_machine.cpp index a6b5210b..81767572 100644 --- a/src/animation/nested_state_machine.cpp +++ b/src/animation/nested_state_machine.cpp @@ -10,12 +10,14 @@ using namespace rive; NestedStateMachine::NestedStateMachine() {} NestedStateMachine::~NestedStateMachine() {} -void NestedStateMachine::advance(float elapsedSeconds) +bool NestedStateMachine::advance(float elapsedSeconds) { + bool keepGoing = false; if (m_StateMachineInstance != nullptr) { - m_StateMachineInstance->advance(elapsedSeconds); + keepGoing = m_StateMachineInstance->advance(elapsedSeconds); } + return keepGoing; } void NestedStateMachine::initializeAnimation(ArtboardInstance* artboard) @@ -30,7 +32,6 @@ void NestedStateMachine::initializeAnimation(ArtboardInstance* artboard) nestedInput->applyValue(); } } - m_nestedInputs.clear(); } StateMachineInstance* NestedStateMachine::stateMachineInstance() @@ -38,6 +39,17 @@ StateMachineInstance* NestedStateMachine::stateMachineInstance() return m_StateMachineInstance.get(); } +#ifdef WITH_RIVE_TOOLS +bool NestedStateMachine::hitTest(Vec2D position) const +{ + if (m_StateMachineInstance != nullptr) + { + return m_StateMachineInstance->hitTest(position); + } + return false; +} +#endif + HitResult NestedStateMachine::pointerMove(Vec2D position) { if (m_StateMachineInstance != nullptr) @@ -74,4 +86,41 @@ HitResult NestedStateMachine::pointerExit(Vec2D position) return HitResult::none; } -void NestedStateMachine::addNestedInput(NestedInput* input) { m_nestedInputs.push_back(input); } \ No newline at end of file +NestedInput* NestedStateMachine::input(size_t index) +{ + if (index < m_nestedInputs.size()) + { + return m_nestedInputs[index]; + } + return nullptr; +} + +NestedInput* NestedStateMachine::input(std::string name) +{ + for (auto input : m_nestedInputs) + { + if (input->name() == name) + { + return input; + } + } + return nullptr; +} + +void NestedStateMachine::addNestedInput(NestedInput* input) { m_nestedInputs.push_back(input); } + +void NestedStateMachine::dataContextFromInstance(ViewModelInstance* viewModelInstance) +{ + if (m_StateMachineInstance != nullptr) + { + m_StateMachineInstance->dataContextFromInstance(viewModelInstance); + } +} + +void NestedStateMachine::dataContext(DataContext* dataContext) +{ + if (m_StateMachineInstance != nullptr) + { + m_StateMachineInstance->dataContext(dataContext); + } +} \ No newline at end of file diff --git a/src/animation/state_machine.cpp b/src/animation/state_machine.cpp index 04506913..9b44672b 100644 --- a/src/animation/state_machine.cpp +++ b/src/animation/state_machine.cpp @@ -91,6 +91,11 @@ void StateMachine::addListener(std::unique_ptr listener) m_Listeners.push_back(std::move(listener)); } +void StateMachine::addDataBind(std::unique_ptr dataBind) +{ + m_dataBinds.push_back(std::move(dataBind)); +} + const StateMachineInput* StateMachine::input(std::string name) const { for (auto& input : m_Inputs) @@ -140,4 +145,13 @@ const StateMachineListener* StateMachine::listener(size_t index) const return m_Listeners[index].get(); } return nullptr; +} + +const DataBind* StateMachine::dataBind(size_t index) const +{ + if (index < m_dataBinds.size()) + { + return m_dataBinds[index].get(); + } + return nullptr; } \ No newline at end of file diff --git a/src/animation/state_machine_input_instance.cpp b/src/animation/state_machine_input_instance.cpp index 5c2fb33a..d2e98d6e 100644 --- a/src/animation/state_machine_input_instance.cpp +++ b/src/animation/state_machine_input_instance.cpp @@ -7,14 +7,24 @@ using namespace rive; SMIInput::SMIInput(const StateMachineInput* input, StateMachineInstance* machineInstance) : - m_MachineInstance(machineInstance), m_Input(input) + m_machineInstance(machineInstance), m_input(input) {} -uint16_t SMIInput::inputCoreType() const { return m_Input->coreType(); } +uint16_t SMIInput::inputCoreType() const { return m_input->coreType(); } -const std::string& SMIInput::name() const { return m_Input->name(); } +const std::string& SMIInput::name() const { return m_input->name(); } -void SMIInput::valueChanged() { m_MachineInstance->markNeedsAdvance(); } +void SMIInput::valueChanged() +{ + m_machineInstance->markNeedsAdvance(); +#ifdef WITH_RIVE_TOOLS + auto callback = m_machineInstance->m_inputChangedCallback; + if (callback != nullptr) + { + callback(m_machineInstance, m_index); + } +#endif +} // bool @@ -54,10 +64,10 @@ SMITrigger::SMITrigger(const StateMachineTrigger* input, StateMachineInstance* m void SMITrigger::fire() { - if (m_Fired) + if (m_fired) { return; } - m_Fired = true; + m_fired = true; valueChanged(); } diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp index 65fc3195..e549a1a7 100644 --- a/src/animation/state_machine_instance.cpp +++ b/src/animation/state_machine_instance.cpp @@ -1,8 +1,12 @@ +#include "rive/animation/animation_reset.hpp" +#include "rive/animation/animation_reset_factory.hpp" #include "rive/animation/animation_state_instance.hpp" #include "rive/animation/animation_state.hpp" #include "rive/animation/any_state.hpp" #include "rive/animation/cubic_interpolator.hpp" #include "rive/animation/entry_state.hpp" +#include "rive/animation/layer_state_flags.hpp" +#include "rive/animation/nested_linear_animation.hpp" #include "rive/animation/nested_state_machine.hpp" #include "rive/animation/state_instance.hpp" #include "rive/animation/state_machine_bool.hpp" @@ -16,7 +20,13 @@ #include "rive/animation/state_machine.hpp" #include "rive/animation/state_transition.hpp" #include "rive/animation/transition_condition.hpp" +#include "rive/animation/transition_comparator.hpp" +#include "rive/animation/transition_property_viewmodel_comparator.hpp" +#include "rive/animation/transition_viewmodel_condition.hpp" #include "rive/animation/state_machine_fire_event.hpp" +#include "rive/data_bind_flags.hpp" +#include "rive/event_report.hpp" +#include "rive/gesture_click_phase.hpp" #include "rive/hit_result.hpp" #include "rive/math/aabb.hpp" #include "rive/math/hit_test.hpp" @@ -24,7 +34,9 @@ #include "rive/nested_artboard.hpp" #include "rive/shapes/shape.hpp" #include "rive/math/math_types.hpp" +#include "rive/audio_event.hpp" #include +#include using namespace rive; namespace rive @@ -49,6 +61,10 @@ class StateMachineLayerInstance m_anyStateInstance = layer->anyState()->makeInstance(instance).release(); m_layer = layer; changeState(m_layer->entryState()); + auto now = std::chrono::high_resolution_clock::now(); + auto nanos = + std::chrono::duration_cast(now.time_since_epoch()).count(); + srand(nanos); } void updateMix(float seconds) @@ -61,6 +77,7 @@ class StateMachineLayerInstance if (m_mix == 1.0f && !m_transitionCompleted) { m_transitionCompleted = true; + clearAnimationReset(); fireEvents(StateMachineFireOccurance::atEnd, m_transition->events()); } } @@ -83,10 +100,7 @@ class StateMachineLayerInstance m_stateFrom->advance(seconds, m_stateMachineInstance); } - if (isTransitionEnded()) - { - apply(); - } + apply(); for (int i = 0; updateState(i != 0); i++) { @@ -99,8 +113,6 @@ class StateMachineLayerInstance } } - apply(); - m_currentState->clearSpilledTime(); return m_mix != 1.0f || m_waitingForExit || @@ -113,12 +125,6 @@ class StateMachineLayerInstance m_mix < 1.0f; } - bool isTransitionEnded() - { - return m_transition != nullptr && m_stateFrom != nullptr && m_transition->duration() != 0 && - m_mix == 1.0f; - } - bool updateState(bool ignoreTriggers) { // Don't allow changing state while a transition is taking place @@ -150,6 +156,13 @@ class StateMachineLayerInstance } } + bool canChangeState(const LayerState* stateTo) + { + return !((m_currentState == nullptr ? nullptr : m_currentState->state()) == stateTo); + } + + double randomValue() { return ((double)rand() / (RAND_MAX)); } + bool changeState(const LayerState* stateTo) { if ((m_currentState == nullptr ? nullptr : m_currentState->state()) == stateTo) @@ -174,86 +187,178 @@ class StateMachineLayerInstance return true; } - bool tryChangeState(StateInstance* stateFromInstance, bool ignoreTriggers) + StateTransition* findRandomTransition(StateInstance* stateFromInstance, bool ignoreTriggers) { - if (stateFromInstance == nullptr) - { - return false; - } + uint32_t totalWeight = 0; auto stateFrom = stateFromInstance->state(); - auto outState = m_currentState; for (size_t i = 0, length = stateFrom->transitionCount(); i < length; i++) { auto transition = stateFrom->transition(i); auto allowed = transition->allowed(stateFromInstance, m_stateMachineInstance, ignoreTriggers); - if (allowed == AllowTransition::yes && changeState(transition->stateTo())) + if (allowed == AllowTransition::yes && canChangeState(transition->stateTo())) + { + transition->evaluatedRandomWeight(transition->randomWeight()); + totalWeight += transition->randomWeight(); + } + else { - m_stateMachineChangedOnAdvance = true; - // state actually has changed - m_transition = transition; - fireEvents(StateMachineFireOccurance::atStart, transition->events()); - if (transition->duration() == 0) + transition->evaluatedRandomWeight(0); + if (allowed == AllowTransition::waitingForExit) { - m_transitionCompleted = true; - fireEvents(StateMachineFireOccurance::atEnd, transition->events()); + m_waitingForExit = true; } - else + } + } + if (totalWeight > 0) + { + double randomWeight = randomValue() * totalWeight * 1.0; + float currentWeight = 0; + size_t index = 0; + StateTransition* transition; + while (index < stateFrom->transitionCount()) + { + transition = stateFrom->transition(index); + auto transitionWeight = transition->evaluatedRandomWeight(); + if (currentWeight + transitionWeight > randomWeight) { - m_transitionCompleted = false; + return transition; } + currentWeight += transitionWeight; + index++; + } + } + return nullptr; + } - if (m_stateFrom != m_anyStateInstance) + StateTransition* findAllowedTransition(StateInstance* stateFromInstance, bool ignoreTriggers) + { + auto stateFrom = stateFromInstance->state(); + // If it should randomize + if ((static_cast(stateFrom->flags()) & LayerStateFlags::Random) == + LayerStateFlags::Random) + { + return findRandomTransition(stateFromInstance, ignoreTriggers); + } + // Else search the first valid transition + for (size_t i = 0, length = stateFrom->transitionCount(); i < length; i++) + { + auto transition = stateFrom->transition(i); + auto allowed = + transition->allowed(stateFromInstance, m_stateMachineInstance, ignoreTriggers); + if (allowed == AllowTransition::yes && canChangeState(transition->stateTo())) + { + transition->evaluatedRandomWeight(transition->randomWeight()); + return transition; + } + else + { + transition->evaluatedRandomWeight(0); + if (allowed == AllowTransition::waitingForExit) { - // Old state from is done. - delete m_stateFrom; + m_waitingForExit = true; } - m_stateFrom = outState; + } + } + return nullptr; + } - // If we had an exit time and wanted to pause on exit, make - // sure to hold the exit time. Delegate this to the - // transition by telling it that it was completed. - if (outState != nullptr && transition->applyExitCondition(outState)) - { - // Make sure we apply this state. This only returns true - // when it's an animation state instance. - auto instance = - static_cast(m_stateFrom)->animationInstance(); + void buildAnimationResetForTransition() + { + m_animationReset = + AnimationResetFactory::fromStates(m_stateFrom, m_currentState, m_artboardInstance); + } - m_holdAnimation = instance->animation(); - m_holdTime = instance->time(); - } - m_mixFrom = m_mix; + void clearAnimationReset() + { + if (m_animationReset != nullptr) + { + AnimationResetFactory::release(std::move(m_animationReset)); + m_animationReset = nullptr; + } + } - // Keep mixing last animation that was mixed in. - if (m_mix != 0.0f) - { - m_holdAnimationFrom = transition->pauseOnExit(); - } - if (m_stateFrom != nullptr && m_stateFrom->state()->is() && - m_currentState != nullptr) - { - auto instance = - static_cast(m_stateFrom)->animationInstance(); + bool tryChangeState(StateInstance* stateFromInstance, bool ignoreTriggers) + { + if (stateFromInstance == nullptr) + { + return false; + } + auto outState = m_currentState; + auto transition = findAllowedTransition(stateFromInstance, ignoreTriggers); + if (transition != nullptr) + { + clearAnimationReset(); + changeState(transition->stateTo()); + m_stateMachineChangedOnAdvance = true; + // state actually has changed + m_transition = transition; + fireEvents(StateMachineFireOccurance::atStart, transition->events()); + if (transition->duration() == 0) + { + m_transitionCompleted = true; + fireEvents(StateMachineFireOccurance::atEnd, transition->events()); + } + else + { + m_transitionCompleted = false; + } - auto spilledTime = instance->spilledTime(); - m_currentState->advance(spilledTime, m_stateMachineInstance); - } - m_mix = 0.0f; - updateMix(0.0f); - m_waitingForExit = false; - return true; + if (m_stateFrom != m_anyStateInstance) + { + // Old state from is done. + delete m_stateFrom; + } + m_stateFrom = outState; + + if (!m_transitionCompleted) + { + buildAnimationResetForTransition(); + } + + // If we had an exit time and wanted to pause on exit, make + // sure to hold the exit time. Delegate this to the + // transition by telling it that it was completed. + if (outState != nullptr && transition->applyExitCondition(outState)) + { + // Make sure we apply this state. This only returns true + // when it's an animation state instance. + auto instance = + static_cast(m_stateFrom)->animationInstance(); + + m_holdAnimation = instance->animation(); + m_holdTime = instance->time(); } - else if (allowed == AllowTransition::waitingForExit) + m_mixFrom = m_mix; + + // Keep mixing last animation that was mixed in. + if (m_mix != 0.0f) { - m_waitingForExit = true; + m_holdAnimationFrom = transition->pauseOnExit(); + } + if (m_stateFrom != nullptr && m_stateFrom->state()->is() && + m_currentState != nullptr) + { + auto instance = + static_cast(m_stateFrom)->animationInstance(); + + auto spilledTime = instance->spilledTime(); + m_currentState->advance(spilledTime, m_stateMachineInstance); } + m_mix = 0.0f; + updateMix(0.0f); + m_waitingForExit = false; + return true; } return false; } void apply(/*Artboard* artboard*/) { + if (m_animationReset != nullptr) + { + m_animationReset->apply(m_artboardInstance); + } if (m_holdAnimation != nullptr) { m_holdAnimation->apply(m_artboardInstance, m_holdTime, m_mixFrom); @@ -269,12 +374,12 @@ class StateMachineLayerInstance if (m_stateFrom != nullptr && m_mix < 1.0f) { auto fromMix = cubic != nullptr ? cubic->transform(m_mixFrom) : m_mixFrom; - m_stateFrom->apply(fromMix); + m_stateFrom->apply(m_artboardInstance, fromMix); } if (m_currentState != nullptr) { auto mix = cubic != nullptr ? cubic->transform(m_mix) : m_mix; - m_currentState->apply(mix); + m_currentState->apply(m_artboardInstance, mix); } } @@ -305,6 +410,7 @@ class StateMachineLayerInstance StateInstance* m_stateFrom = nullptr; const StateTransition* m_transition = nullptr; + std::unique_ptr m_animationReset = nullptr; bool m_transitionCompleted = false; bool m_holdAnimationFrom = false; @@ -319,19 +425,44 @@ class StateMachineLayerInstance float m_holdTime = 0.0f; }; -class HitComponent +class ListenerGroup { public: - Component* component() const { return m_component; } - HitComponent(Component* component, StateMachineInstance* stateMachineInstance) : - m_component(component), m_stateMachineInstance(stateMachineInstance) - {} - virtual ~HitComponent(){}; - virtual HitResult processEvent(Vec2D position, ListenerType hitType, bool canHit) = 0; + ListenerGroup(const StateMachineListener* listener) : m_listener(listener) {} + void consume() { m_isConsumed = true; } + // + void hover() { m_isHovered = true; } + void unhover() { m_isHovered = false; } + void reset() + { + m_isConsumed = false; + m_prevIsHovered = m_isHovered; + m_isHovered = false; + if (m_clickPhase == GestureClickPhase::clicked) + { + m_clickPhase = GestureClickPhase::out; + } + } + bool isConsumed() { return m_isConsumed; } + bool isHovered() { return m_isHovered; } + bool prevHovered() { return m_prevIsHovered; } + void clickPhase(GestureClickPhase value) { m_clickPhase = value; } + GestureClickPhase clickPhase() { return m_clickPhase; } + const StateMachineListener* listener() const { return m_listener; }; + // A vector storing the previous position for this specific listener gorup + Vec2D previousPosition; -protected: - Component* m_component; - StateMachineInstance* m_stateMachineInstance; +private: + // Consumed listeners aren't processed again in the current frame + bool m_isConsumed = false; + // This variable holds the hover status of the the listener itself so it can + // be shared between all shapes that target it + bool m_isHovered = false; + // Variable storing the previous hovered state to check for hover changes + bool m_prevIsHovered = false; + // A click gesture is composed of three phases and is shared between all shapes + GestureClickPhase m_clickPhase = GestureClickPhase::out; + const StateMachineListener* m_listener; }; /// Representation of a Shape from the Artboard Instance and all the listeners it @@ -342,49 +473,184 @@ class HitShape : public HitComponent public: HitShape(Component* shape, StateMachineInstance* stateMachineInstance) : HitComponent(shape, stateMachineInstance) - {} - ~HitShape() {} + { + if (shape->as()->isTargetOpaque()) + { + canEarlyOut = false; + } + } bool isHovered = false; + bool canEarlyOut = true; + bool hasDownListener = false; + bool hasUpListener = false; float hitRadius = 2; - std::vector listeners; - HitResult processEvent(Vec2D position, ListenerType hitType, bool canHit) override + std::vector listeners; + + bool hitTest(Vec2D position) const +#ifdef WITH_RIVE_TOOLS + override +#endif { auto shape = m_component->as(); + auto worldBounds = shape->worldBounds(); + if (!worldBounds.contains(position)) + { + return false; + } auto hitArea = AABB(position.x - hitRadius, position.y - hitRadius, position.x + hitRadius, position.y + hitRadius) .round(); - bool isOver = canHit ? shape->hitTest(hitArea) : false; - bool hoverChange = isHovered != isOver; - isHovered = isOver; + return shape->hitTest(hitArea); + } + + void prepareEvent(Vec2D position, ListenerType hitType) override + { + if (canEarlyOut && (hitType != ListenerType::down || !hasDownListener) && + (hitType != ListenerType::up || !hasUpListener)) + { +#ifdef TESTING + earlyOutCount++; +#endif + return; + } + isHovered = hitTest(position); // // iterate all listeners associated with this hit shape - for (auto listener : listeners) + if (isHovered) { - // Always update hover states regardless of which specific listener type - // we're trying to trigger. - if (hoverChange) + for (auto listenerGroup : listeners) + { + + listenerGroup->hover(); + } + } + } + + HitResult processEvent(Vec2D position, ListenerType hitType, bool canHit) override + { + // If the shape doesn't have any ListenerType::move / enter / exit and the event + // being processed is not of the type it needs to handle. There is no need to perform + // a hitTest (which is relatively expensive and would be happening on every + // pointer move) so we early out. + if (canEarlyOut && (hitType != ListenerType::down || !hasDownListener) && + (hitType != ListenerType::up || !hasUpListener)) + { + return HitResult::none; + } + auto shape = m_component->as(); + + // // iterate all listeners associated with this hit shape + for (auto listenerGroup : listeners) + { + if (listenerGroup->isConsumed()) { - if (isOver && listener->listenerType() == ListenerType::enter) + continue; + } + // Because each group is tested individually for its hover state, a group + // could be marked "incorrectly" as hovered at this point. + // But once we iterate each element in the drawing order, that group can + // be occluded by an opaque target on top of it. + // So although it is hovered in isolation, it shouldn't be considered as + // hovered in the full context. + // In this case, we unhover the group so it is not marked as previously + // hovered. + if (!canHit && listenerGroup->isHovered()) + { + listenerGroup->unhover(); + } + + bool isGroupHovered = canHit ? listenerGroup->isHovered() : false; + bool hoverChange = listenerGroup->prevHovered() != isGroupHovered; + // If hover has changes, it means that the element is hovered for the + // first time. Previous positions need to be reset to avoid jumps. + if (hoverChange && isGroupHovered) + { + listenerGroup->previousPosition.x = position.x; + listenerGroup->previousPosition.y = position.y; + } + + // Handle click gesture phases. A click gesture has two phases. + // First one attached to a pointer down actions, second one attached to a + // pointer up action. Both need to act on a shape of the listener group. + if (isGroupHovered) + { + if (hitType == ListenerType::down) { - listener->performChanges(m_stateMachineInstance, position); - m_stateMachineInstance->markNeedsAdvance(); + listenerGroup->clickPhase(GestureClickPhase::down); } - else if (!isOver && listener->listenerType() == ListenerType::exit) + else if (hitType == ListenerType::up && + listenerGroup->clickPhase() == GestureClickPhase::down) { - listener->performChanges(m_stateMachineInstance, position); - m_stateMachineInstance->markNeedsAdvance(); + listenerGroup->clickPhase(GestureClickPhase::clicked); } } - if (isOver && hitType == listener->listenerType()) + else + { + if (hitType == ListenerType::down || hitType == ListenerType::up) + { + listenerGroup->clickPhase(GestureClickPhase::out); + } + } + auto listener = listenerGroup->listener(); + // Always update hover states regardless of which specific listener type + // we're trying to trigger. + // If hover has changed and: + // - it's hovering and the listener is of type enter + // - it's not hovering and the listener is of type exit + if (hoverChange && + ((isGroupHovered && listener->listenerType() == ListenerType::enter) || + (!isGroupHovered && listener->listenerType() == ListenerType::exit))) + { + listener->performChanges(m_stateMachineInstance, + position, + listenerGroup->previousPosition); + m_stateMachineInstance->markNeedsAdvance(); + listenerGroup->consume(); + } + // Perform changes if: + // - the click gesture is complete and the listener is of type click + // - the event type matches the listener type and it is hovering the group + if ((listenerGroup->clickPhase() == GestureClickPhase::clicked && + listener->listenerType() == ListenerType::click) || + (isGroupHovered && hitType == listener->listenerType())) { - listener->performChanges(m_stateMachineInstance, position); + listener->performChanges(m_stateMachineInstance, + position, + listenerGroup->previousPosition); m_stateMachineInstance->markNeedsAdvance(); + listenerGroup->consume(); + } + listenerGroup->previousPosition.x = position.x; + listenerGroup->previousPosition.y = position.y; + } + return (isHovered && canHit) + ? shape->isTargetOpaque() ? HitResult::hitOpaque : HitResult::hit + : HitResult::none; + } + + void addListener(ListenerGroup* listenerGroup) + { + auto stateMachineListener = listenerGroup->listener(); + auto listenerType = stateMachineListener->listenerType(); + if (listenerType == ListenerType::enter || listenerType == ListenerType::exit || + listenerType == ListenerType::move) + { + canEarlyOut = false; + } + else + { + if (listenerType == ListenerType::down || listenerType == ListenerType::click) + { + hasDownListener = true; + } + if (listenerType == ListenerType::up || listenerType == ListenerType::click) + { + hasUpListener = true; } } - return isOver ? shape->isTargetOpaque() ? HitResult::hitOpaque : HitResult::hit - : HitResult::none; + listeners.push_back(listenerGroup); } }; class HitNestedArtboard : public HitComponent @@ -393,7 +659,37 @@ class HitNestedArtboard : public HitComponent HitNestedArtboard(Component* nestedArtboard, StateMachineInstance* stateMachineInstance) : HitComponent(nestedArtboard, stateMachineInstance) {} - ~HitNestedArtboard() {} + ~HitNestedArtboard() override {} + +#ifdef WITH_RIVE_TOOLS + bool hitTest(Vec2D position) const override + { + auto nestedArtboard = m_component->as(); + if (nestedArtboard->isCollapsed()) + { + return false; + } + Vec2D nestedPosition; + if (!nestedArtboard->worldToLocal(position, &nestedPosition)) + { + // Mounted artboard isn't ready or has a 0 scale transform. + return false; + } + + for (auto nestedAnimation : nestedArtboard->nestedAnimations()) + { + if (nestedAnimation->is()) + { + auto nestedStateMachine = nestedAnimation->as(); + if (nestedStateMachine->hitTest(nestedPosition)) + { + return true; + } + } + } + return false; + } +#endif HitResult processEvent(Vec2D position, ListenerType hitType, bool canHit) override { auto nestedArtboard = m_component->as(); @@ -430,6 +726,7 @@ class HitNestedArtboard : public HitComponent case ListenerType::enter: case ListenerType::exit: case ListenerType::event: + case ListenerType::click: break; } } @@ -445,6 +742,7 @@ class HitNestedArtboard : public HitComponent case ListenerType::enter: case ListenerType::exit: case ListenerType::event: + case ListenerType::click: break; } } @@ -452,7 +750,9 @@ class HitNestedArtboard : public HitComponent } return hitResult; } + void prepareEvent(Vec2D position, ListenerType hitType) override {} }; + } // namespace rive HitResult StateMachineInstance::updateListeners(Vec2D position, ListenerType hitType) @@ -462,12 +762,21 @@ HitResult StateMachineInstance::updateListeners(Vec2D position, ListenerType hit position -= Vec2D(m_artboardInstance->originX() * m_artboardInstance->width(), m_artboardInstance->originY() * m_artboardInstance->height()); } - + // First reset all listener groups before processing the events + for (const auto& listenerGroup : m_listenerGroups) + { + listenerGroup.get()->reset(); + } + // Next prepare the event to set the common hover status for each group + for (const auto& hitShape : m_hitComponents) + { + hitShape->prepareEvent(position, hitType); + } bool hitSomething = false; bool hitOpaque = false; + // Finally process the events for (const auto& hitShape : m_hitComponents) { - // TODO: quick reject. HitResult hitResult = hitShape->processEvent(position, hitType, !hitOpaque); @@ -483,6 +792,28 @@ HitResult StateMachineInstance::updateListeners(Vec2D position, ListenerType hit return hitSomething ? hitOpaque ? HitResult::hitOpaque : HitResult::hit : HitResult::none; } +#ifdef WITH_RIVE_TOOLS +bool StateMachineInstance::hitTest(Vec2D position) const +{ + if (m_artboardInstance->frameOrigin()) + { + position -= Vec2D(m_artboardInstance->originX() * m_artboardInstance->width(), + m_artboardInstance->originY() * m_artboardInstance->height()); + } + + for (const auto& hitShape : m_hitComponents) + { + // TODO: quick reject. + + if (hitShape->hitTest(position)) + { + return true; + } + } + return false; +} +#endif + HitResult StateMachineInstance::pointerMove(Vec2D position) { return updateListeners(position, ListenerType::move); @@ -500,6 +831,17 @@ HitResult StateMachineInstance::pointerExit(Vec2D position) return updateListeners(position, ListenerType::exit); } +#ifdef TESTING +const LayerState* StateMachineInstance::layerState(size_t index) +{ + if (index < m_machine->layerCount()) + { + return m_layers[index].currentState(); + } + return nullptr; +} +#endif + StateMachineInstance::StateMachineInstance(const StateMachine* machine, ArtboardInstance* instance) : Scene(instance), m_machine(machine) @@ -528,6 +870,13 @@ StateMachineInstance::StateMachineInstance(const StateMachine* machine, // Sanity check. break; } +#ifdef WITH_RIVE_TOOLS + auto instance = m_inputInstances[i]; + if (instance != nullptr) + { + instance->m_index = i; + } +#endif } m_layerCount = machine->layerCount(); @@ -537,63 +886,99 @@ StateMachineInstance::StateMachineInstance(const StateMachine* machine, m_layers[i].init(this, machine->layer(i), m_artboardInstance); } + // Initialize dataBinds. All databinds are cloned for the state machine instance. + // That enables binding each instance to its own context without polluting the rest. + auto dataBindCount = machine->dataBindCount(); + for (size_t i = 0; i < dataBindCount; i++) + { + auto dataBind = machine->dataBind(i); + auto dataBindClone = static_cast(dataBind->clone()); + m_dataBinds.push_back(dataBindClone); + if (dataBind->target()->is()) + { + auto bindableProperty = dataBind->target()->as(); + auto bindablePropertyInstance = m_bindablePropertyInstances.find(bindableProperty); + BindableProperty* bindablePropertyClone; + if (bindablePropertyInstance == m_bindablePropertyInstances.end()) + { + bindablePropertyClone = bindableProperty->clone()->as(); + m_bindablePropertyInstances[bindableProperty] = bindablePropertyClone; + } + else + { + bindablePropertyClone = bindablePropertyInstance->second; + } + dataBindClone->target(bindablePropertyClone); + // We are only storing in this unordered map data binds that are targetting the source. + // For now, this is only the case for listener actions. + if (static_cast(dataBindClone->flags()) == DataBindFlags::ToSource) + { + m_bindableDataBinds[bindablePropertyClone] = dataBindClone; + } + } + } + // Initialize listeners. Store a lookup table of shape id to hit shape // representation (an object that stores all the listeners triggered by the // shape producing a listener). - std::unordered_map hitShapeLookup; + std::unordered_map hitShapeLookup; for (std::size_t i = 0; i < machine->listenerCount(); i++) { auto listener = machine->listener(i); - - // Iterate actual leaf hittable shapes tied to this listener and resolve - // corresponding ones in the artboard instance. - for (auto id : listener->hitShapeIds()) + if (listener->listenerType() == ListenerType::event) { - HitShape* hitShape; - auto itr = hitShapeLookup.find(id); - if (itr == hitShapeLookup.end()) - { - auto shape = m_artboardInstance->resolve(id); - if (shape != nullptr && shape->is()) - { - auto hs = rivestd::make_unique(shape->as(), this); - hitShapeLookup[id] = hitShape = hs.get(); - m_hitComponents.push_back(std::move(hs)); - } - else + continue; + } + auto listenerGroup = rivestd::make_unique(listener); + auto target = m_artboardInstance->resolve(listener->targetId()); + if (target != nullptr && target->is()) + { + target->as()->forAll([&](Component* component) { + if (component->is()) { - // No object or not a shape... - continue; + HitShape* hitShape; + auto itr = hitShapeLookup.find(component); + if (itr == hitShapeLookup.end()) + { + component->as()->addFlags(PathFlags::neverDeferUpdate); + component->as()->addDirt(ComponentDirt::Path, true); + auto hs = rivestd::make_unique(component, this); + hitShapeLookup[component] = hitShape = hs.get(); + m_hitComponents.push_back(std::move(hs)); + } + else + { + hitShape = itr->second; + } + hitShape->addListener(listenerGroup.get()); } - } - else - { - hitShape = itr->second; - } - hitShape->listeners.push_back(listener); + return true; + }); } + m_listenerGroups.push_back(std::move(listenerGroup)); } for (auto nestedArtboard : instance->nestedArtboards()) { if (nestedArtboard->hasNestedStateMachines()) { - auto hn = rivestd::make_unique(nestedArtboard->as(), this); m_hitComponents.push_back(std::move(hn)); - - for (auto animation : nestedArtboard->nestedAnimations()) + } + for (auto animation : nestedArtboard->nestedAnimations()) + { + if (animation->is()) { - if (animation->is()) - { - animation->as() - ->stateMachineInstance() - ->setParentNestedArtboard(nestedArtboard); - animation->as() - ->stateMachineInstance() - ->setParentStateMachineInstance(this); - } + auto notifier = animation->as()->stateMachineInstance(); + notifier->setNestedArtboard(nestedArtboard); + notifier->addNestedEventListener(this); + } + else if (animation->is()) + { + auto notifier = animation->as()->animationInstance(); + notifier->setNestedArtboard(nestedArtboard); + notifier->addNestedEventListener(this); } } } @@ -644,8 +1029,22 @@ void StateMachineInstance::sortHitComponents() } } +void StateMachineInstance::updateDataBinds() +{ + for (auto dataBind : m_dataBinds) + { + auto d = dataBind->dirt(); + if (d != ComponentDirt::None) + { + dataBind->dirt(ComponentDirt::None); + dataBind->update(d); + } + } +} + bool StateMachineInstance::advance(float seconds) { + updateDataBinds(); if (m_artboardInstance->hasChangedDrawOrderInLastUpdate()) { sortHitComponents(); @@ -671,9 +1070,9 @@ bool StateMachineInstance::advance(float seconds) bool StateMachineInstance::advanceAndApply(float seconds) { - bool more = this->advance(seconds); - m_artboardInstance->advance(seconds); - return more; + bool keepGoing = this->advance(seconds); + keepGoing = m_artboardInstance->advance(seconds) || keepGoing; + return keepGoing; } void StateMachineInstance::markNeedsAdvance() { m_needsAdvance = true; } @@ -717,6 +1116,23 @@ SMITrigger* StateMachineInstance::getTrigger(const std::string& name) const return getNamedInput(name); } +void StateMachineInstance::dataContextFromInstance(ViewModelInstance* viewModelInstance) +{ + dataContext(new DataContext(viewModelInstance)); +} + +void StateMachineInstance::dataContext(DataContext* dataContext) +{ + m_DataContext = dataContext; + for (auto dataBind : m_dataBinds) + { + if (dataBind->is()) + { + dataBind->as()->bindFromContext(dataContext); + } + } +} + size_t StateMachineInstance::stateChangedCount() const { size_t count = 0; @@ -793,7 +1209,12 @@ const EventReport StateMachineInstance::reportedEventAt(std::size_t index) const return m_reportedEvents[index]; } -void StateMachineInstance::notifyEventListeners(std::vector events, +void StateMachineInstance::notify(const std::vector& events, NestedArtboard* context) +{ + notifyEventListeners(events, context); +} + +void StateMachineInstance::notifyEventListeners(const std::vector& events, NestedArtboard* source) { if (events.size() > 0) @@ -808,7 +1229,8 @@ void StateMachineInstance::notifyEventListeners(std::vector events, { for (const auto event : events) { - auto sourceArtboard = source == nullptr ? artboard() : source->artboard(); + auto sourceArtboard = + source == nullptr ? artboard() : source->artboardInstance(); // listener->eventId() can point to an id from an event in the context of this // artboard or the context of a nested artboard. Because those ids belong to @@ -826,16 +1248,46 @@ void StateMachineInstance::notifyEventListeners(std::vector events, auto listenerEvent = sourceArtboard->resolve(listener->eventId()); if (listenerEvent == event.event()) { - listener->performChanges(this, Vec2D()); + listener->performChanges(this, Vec2D(), Vec2D()); break; } } } } // Bubble the event up to parent artboard state machines immediately - if (m_parentStateMachineInstance != nullptr) + for (auto listener : nestedEventListeners()) { - m_parentStateMachineInstance->notifyEventListeners(events, m_parentNestedArtboard); + listener->notify(events, nestedArtboard()); } + + for (auto report : events) + { + auto event = report.event(); + if (event->is()) + { + event->as()->play(); + } + } + } +} + +BindableProperty* StateMachineInstance::bindablePropertyInstance( + BindableProperty* bindableProperty) const +{ + auto bindablePropertyInstance = m_bindablePropertyInstances.find(bindableProperty); + if (bindablePropertyInstance == m_bindablePropertyInstances.end()) + { + return nullptr; + } + return bindablePropertyInstance->second; +} + +DataBind* StateMachineInstance::bindableDataBind(BindableProperty* bindableProperty) +{ + auto dataBind = m_bindableDataBinds.find(bindableProperty); + if (dataBind == m_bindableDataBinds.end()) + { + return nullptr; } + return dataBind->second; } diff --git a/src/animation/state_machine_listener.cpp b/src/animation/state_machine_listener.cpp index 3e51892c..7abf5215 100644 --- a/src/animation/state_machine_listener.cpp +++ b/src/animation/state_machine_listener.cpp @@ -14,7 +14,7 @@ StateMachineListener::~StateMachineListener() {} void StateMachineListener::addAction(std::unique_ptr action) { - m_Actions.push_back(std::move(action)); + m_actions.push_back(std::move(action)); } StatusCode StateMachineListener::import(ImportStack& importStack) @@ -31,54 +31,19 @@ StatusCode StateMachineListener::import(ImportStack& importStack) const ListenerAction* StateMachineListener::action(size_t index) const { - if (index < m_Actions.size()) + if (index < m_actions.size()) { - return m_Actions[index].get(); + return m_actions[index].get(); } return nullptr; } -StatusCode StateMachineListener::onAddedClean(CoreContext* context) -{ - auto artboard = static_cast(context); - auto target = artboard->resolve(targetId()); - - for (auto core : artboard->objects()) - { - if (core == nullptr) - { - continue; - } - - // Iterate artboard to find Shapes that are parented to the target - if (core->is()) - { - auto shape = core->as(); - - for (ContainerComponent* component = shape; component != nullptr; - component = component->parent()) - { - if (component == target) - { - auto index = artboard->idOf(shape); - if (index != 0) - { - m_HitShapesIds.push_back(index); - } - break; - } - } - } - } - - return Super::onAddedClean(context); -} - void StateMachineListener::performChanges(StateMachineInstance* stateMachineInstance, - Vec2D position) const + Vec2D position, + Vec2D previousPosition) const { - for (auto& action : m_Actions) + for (auto& action : m_actions) { - action->perform(stateMachineInstance, position); + action->perform(stateMachineInstance, position, previousPosition); } } \ No newline at end of file diff --git a/src/animation/state_transition.cpp b/src/animation/state_transition.cpp index 1d9bf088..f419c4b5 100644 --- a/src/animation/state_transition.cpp +++ b/src/animation/state_transition.cpp @@ -8,7 +8,10 @@ #include "rive/animation/state_transition.hpp" #include "rive/animation/transition_condition.hpp" #include "rive/animation/transition_trigger_condition.hpp" +#include "rive/animation/transition_input_condition.hpp" +#include "rive/animation/transition_viewmodel_condition.hpp" #include "rive/animation/state_machine_instance.hpp" +#include "rive/animation/transition_property_viewmodel_comparator.hpp" #include "rive/importers/import_stack.hpp" #include "rive/importers/layer_state_importer.hpp" @@ -147,11 +150,8 @@ AllowTransition StateTransition::allowed(StateInstance* stateFrom, for (auto condition : m_Conditions) { - // N.B. state machine instance sanitizes these for us... - auto input = stateMachineInstance->input(condition->inputId()); - if ((ignoreTriggers && condition->is()) || - !condition->evaluate(input)) + !condition->evaluate(stateMachineInstance)) { return AllowTransition::no; } diff --git a/src/animation/system_state_instance.cpp b/src/animation/system_state_instance.cpp index 92e28de5..4eb489a4 100644 --- a/src/animation/system_state_instance.cpp +++ b/src/animation/system_state_instance.cpp @@ -6,6 +6,6 @@ SystemStateInstance::SystemStateInstance(const LayerState* layerState, ArtboardI {} void SystemStateInstance::advance(float seconds, StateMachineInstance* stateMachineInstance) {} -void SystemStateInstance::apply(float mix) {} +void SystemStateInstance::apply(ArtboardInstance* artboard, float mix) {} bool SystemStateInstance::keepGoing() const { return false; } \ No newline at end of file diff --git a/src/animation/transition_bool_condition.cpp b/src/animation/transition_bool_condition.cpp index 14eb732e..f50a84e8 100644 --- a/src/animation/transition_bool_condition.cpp +++ b/src/animation/transition_bool_condition.cpp @@ -13,8 +13,9 @@ bool TransitionBoolCondition::validateInputType(const StateMachineInput* input) return input == nullptr || input->is(); } -bool TransitionBoolCondition::evaluate(const SMIInput* inputInstance) const +bool TransitionBoolCondition::evaluate(const StateMachineInstance* stateMachineInstance) const { + auto inputInstance = stateMachineInstance->input(inputId()); if (inputInstance == nullptr) { return true; diff --git a/src/animation/transition_comparator.cpp b/src/animation/transition_comparator.cpp new file mode 100644 index 00000000..b14254aa --- /dev/null +++ b/src/animation/transition_comparator.cpp @@ -0,0 +1,101 @@ +#include "rive/animation/transition_comparator.hpp" +#include "rive/animation/transition_viewmodel_condition.hpp" +#include "rive/animation/state_machine_instance.hpp" +#include "rive/importers/transition_viewmodel_condition_importer.hpp" + +using namespace rive; + +StatusCode TransitionComparator::import(ImportStack& importStack) +{ + auto transitionViewModelConditionImporter = + importStack.latest( + TransitionViewModelCondition::typeKey); + if (transitionViewModelConditionImporter == nullptr) + { + return StatusCode::MissingObject; + } + transitionViewModelConditionImporter->setComparator(this); + return Super::import(importStack); +} + +bool TransitionComparator::compareNumbers(float left, float right, TransitionConditionOp op) +{ + switch (op) + { + case TransitionConditionOp::equal: + return left == right; + case TransitionConditionOp::notEqual: + return left != right; + case TransitionConditionOp::lessThanOrEqual: + return left <= right; + case TransitionConditionOp::lessThan: + return left < right; + case TransitionConditionOp::greaterThanOrEqual: + return left >= right; + case TransitionConditionOp::greaterThan: + return left > right; + default: + return false; + } +} + +bool TransitionComparator::compareStrings(std::string left, + std::string right, + TransitionConditionOp op) +{ + switch (op) + { + case TransitionConditionOp::equal: + return left == right; + case TransitionConditionOp::notEqual: + return left != right; + default: + return false; + } +} + +bool TransitionComparator::compareBooleans(bool left, bool right, TransitionConditionOp op) +{ + switch (op) + { + case TransitionConditionOp::equal: + return left == right; + case TransitionConditionOp::notEqual: + return left != right; + default: + return false; + } +} + +bool TransitionComparator::compareEnums(uint16_t left, uint16_t right, TransitionConditionOp op) +{ + switch (op) + { + case TransitionConditionOp::equal: + return left == right; + case TransitionConditionOp::notEqual: + return left != right; + default: + return false; + } +} + +bool TransitionComparator::compareColors(int left, int right, TransitionConditionOp op) +{ + switch (op) + { + case TransitionConditionOp::equal: + return left == right; + case TransitionConditionOp::notEqual: + return left != right; + default: + return false; + } +} + +bool TransitionComparator::compare(TransitionComparator* comparand, + TransitionConditionOp operation, + const StateMachineInstance* stateMachineInstance) +{ + return false; +} \ No newline at end of file diff --git a/src/animation/transition_condition.cpp b/src/animation/transition_condition.cpp index 23b0fe68..94865162 100644 --- a/src/animation/transition_condition.cpp +++ b/src/animation/transition_condition.cpp @@ -1,8 +1,6 @@ -#include "rive/animation/transition_bool_condition.hpp" +#include "rive/animation/transition_condition.hpp" #include "rive/animation/state_transition.hpp" #include "rive/importers/state_transition_importer.hpp" -#include "rive/importers/state_machine_importer.hpp" -#include "rive/animation/state_machine.hpp" using namespace rive; @@ -12,22 +10,6 @@ StatusCode TransitionCondition::onAddedClean(CoreContext* context) { return Stat StatusCode TransitionCondition::import(ImportStack& importStack) { - auto stateMachineImporter = importStack.latest(StateMachine::typeKey); - if (stateMachineImporter == nullptr) - { - return StatusCode::MissingObject; - } - - // Make sure the inputId doesn't overflow the input buffer. - if ((size_t)inputId() >= stateMachineImporter->stateMachine()->inputCount()) - { - return StatusCode::InvalidObject; - } - if (!validateInputType(stateMachineImporter->stateMachine()->input((size_t)inputId()))) - { - return StatusCode::InvalidObject; - } - auto transitionImporter = importStack.latest(StateTransition::typeKey); if (transitionImporter == nullptr) { diff --git a/src/animation/transition_input_condition.cpp b/src/animation/transition_input_condition.cpp new file mode 100644 index 00000000..52fd89e3 --- /dev/null +++ b/src/animation/transition_input_condition.cpp @@ -0,0 +1,26 @@ +#include "rive/animation/transition_input_condition.hpp" +#include "rive/importers/state_machine_importer.hpp" +#include "rive/animation/state_machine.hpp" + +using namespace rive; + +StatusCode TransitionInputCondition::import(ImportStack& importStack) +{ + auto stateMachineImporter = importStack.latest(StateMachine::typeKey); + if (stateMachineImporter == nullptr) + { + return StatusCode::MissingObject; + } + + // Make sure the inputId doesn't overflow the input buffer. + if ((size_t)inputId() >= stateMachineImporter->stateMachine()->inputCount()) + { + return StatusCode::InvalidObject; + } + if (!validateInputType(stateMachineImporter->stateMachine()->input((size_t)inputId()))) + { + return StatusCode::InvalidObject; + } + + return Super::import(importStack); +} \ No newline at end of file diff --git a/src/animation/transition_number_condition.cpp b/src/animation/transition_number_condition.cpp index 102d6314..da820e39 100644 --- a/src/animation/transition_number_condition.cpp +++ b/src/animation/transition_number_condition.cpp @@ -13,8 +13,9 @@ bool TransitionNumberCondition::validateInputType(const StateMachineInput* input return input == nullptr || input->is(); } -bool TransitionNumberCondition::evaluate(const SMIInput* inputInstance) const +bool TransitionNumberCondition::evaluate(const StateMachineInstance* stateMachineInstance) const { + auto inputInstance = stateMachineInstance->input(inputId()); if (inputInstance == nullptr) { return true; diff --git a/src/animation/transition_property_artboard_comparator.cpp b/src/animation/transition_property_artboard_comparator.cpp new file mode 100644 index 00000000..a769c7e4 --- /dev/null +++ b/src/animation/transition_property_artboard_comparator.cpp @@ -0,0 +1,54 @@ +#include "rive/animation/transition_property_artboard_comparator.hpp" +#include "rive/animation/transition_property_viewmodel_comparator.hpp" +#include "rive/animation/transition_value_number_comparator.hpp" +#include "rive/animation/artboard_property.hpp" +#include "rive/animation/state_machine_instance.hpp" +#include "rive/data_bind/bindable_property_number.hpp" + +using namespace rive; + +float TransitionPropertyArtboardComparator::propertyValue( + const StateMachineInstance* stateMachineInstance) +{ + auto artboard = stateMachineInstance->artboard(); + if (artboard != nullptr) + { + + auto property = static_cast(propertyType()); + switch (property) + { + case ArtboardProperty::width: + return artboard->layoutWidth(); + break; + case ArtboardProperty::height: + return artboard->layoutHeight(); + break; + case ArtboardProperty::ratio: + return artboard->layoutWidth() / artboard->layoutHeight(); + break; + + default: + break; + } + } + return 0; +} + +bool TransitionPropertyArtboardComparator::compare(TransitionComparator* comparand, + TransitionConditionOp operation, + const StateMachineInstance* stateMachineInstance) +{ + auto value = propertyValue(stateMachineInstance); + if (comparand->is()) + { + auto rightValue = comparand->as() + ->value(stateMachineInstance); + return compareNumbers(value, rightValue, operation); + } + else if (comparand->is()) + { + auto rightValue = comparand->as()->value(); + return compareNumbers(value, rightValue, operation); + } + return false; +} \ No newline at end of file diff --git a/src/animation/transition_property_comparator.cpp b/src/animation/transition_property_comparator.cpp new file mode 100644 index 00000000..05d25ae2 --- /dev/null +++ b/src/animation/transition_property_comparator.cpp @@ -0,0 +1,3 @@ +#include "rive/animation/transition_property_comparator.hpp" + +using namespace rive; \ No newline at end of file diff --git a/src/animation/transition_property_viewmodel_comparator.cpp b/src/animation/transition_property_viewmodel_comparator.cpp new file mode 100644 index 00000000..80f92ecb --- /dev/null +++ b/src/animation/transition_property_viewmodel_comparator.cpp @@ -0,0 +1,127 @@ +#include "rive/animation/transition_property_viewmodel_comparator.hpp" +#include "rive/animation/transition_value_number_comparator.hpp" +#include "rive/animation/transition_value_string_comparator.hpp" +#include "rive/animation/transition_value_color_comparator.hpp" +#include "rive/animation/transition_value_boolean_comparator.hpp" +#include "rive/animation/transition_value_enum_comparator.hpp" +#include "rive/animation/state_machine_instance.hpp" +#include "rive/importers/bindable_property_importer.hpp" +#include "rive/data_bind/bindable_property_number.hpp" +#include "rive/data_bind/bindable_property_string.hpp" +#include "rive/data_bind/bindable_property_color.hpp" +#include "rive/data_bind/bindable_property_enum.hpp" +#include "rive/data_bind/bindable_property_boolean.hpp" + +using namespace rive; + +StatusCode TransitionPropertyViewModelComparator::import(ImportStack& importStack) +{ + auto bindablePropertyImporter = + importStack.latest(BindablePropertyBase::typeKey); + if (bindablePropertyImporter == nullptr) + { + return StatusCode::MissingObject; + } + m_bindableProperty = bindablePropertyImporter->bindableProperty(); + + return Super::import(importStack); +} + +bool TransitionPropertyViewModelComparator::compare( + TransitionComparator* comparand, + TransitionConditionOp operation, + const StateMachineInstance* stateMachineInstance) +{ + switch (m_bindableProperty->coreType()) + { + case BindablePropertyNumber::typeKey: + if (comparand->is()) + { + auto rightValue = comparand->as() + ->value(stateMachineInstance); + return compareNumbers(value(stateMachineInstance), + rightValue, + operation); + } + else if (comparand->is()) + { + auto rightValue = comparand->as()->value(); + return compareNumbers(value(stateMachineInstance), + rightValue, + operation); + } + break; + case BindablePropertyString::typeKey: + if (comparand->is()) + { + auto rightValue = + comparand->as() + ->value(stateMachineInstance); + return compareStrings( + value(stateMachineInstance), + rightValue, + operation); + } + else if (comparand->is()) + { + auto rightValue = comparand->as()->value(); + return compareStrings( + value(stateMachineInstance), + rightValue, + operation); + } + break; + case BindablePropertyColor::typeKey: + if (comparand->is()) + { + auto rightValue = comparand->as() + ->value(stateMachineInstance); + return compareColors(value(stateMachineInstance), + rightValue, + operation); + } + else if (comparand->is()) + { + auto rightValue = comparand->as()->value(); + return compareColors(value(stateMachineInstance), + rightValue, + operation); + } + break; + case BindablePropertyBoolean::typeKey: + if (comparand->is()) + { + auto rightValue = comparand->as() + ->value(stateMachineInstance); + return compareBooleans(value(stateMachineInstance), + rightValue, + operation); + } + else if (comparand->is()) + { + auto rightValue = comparand->as()->value(); + return compareBooleans(value(stateMachineInstance), + rightValue, + operation); + } + break; + case BindablePropertyEnum::typeKey: + if (comparand->is()) + { + auto rightValue = comparand->as() + ->value(stateMachineInstance); + return compareEnums(value(stateMachineInstance), + rightValue, + operation); + } + else if (comparand->is()) + { + auto rightValue = comparand->as()->value(); + return compareEnums(value(stateMachineInstance), + rightValue, + operation); + } + break; + } + return false; +} \ No newline at end of file diff --git a/src/animation/transition_trigger_condition.cpp b/src/animation/transition_trigger_condition.cpp index a9ed142b..15be1a62 100644 --- a/src/animation/transition_trigger_condition.cpp +++ b/src/animation/transition_trigger_condition.cpp @@ -13,15 +13,16 @@ bool TransitionTriggerCondition::validateInputType(const StateMachineInput* inpu return input == nullptr || input->is(); } -bool TransitionTriggerCondition::evaluate(const SMIInput* inputInstance) const +bool TransitionTriggerCondition::evaluate(const StateMachineInstance* stateMachineInstance) const { + auto inputInstance = stateMachineInstance->input(inputId()); if (inputInstance == nullptr) { return true; } auto triggerInput = static_cast(inputInstance); - if (triggerInput->m_Fired) + if (triggerInput->m_fired) { return true; } diff --git a/src/animation/transition_value_boolean_comparator.cpp b/src/animation/transition_value_boolean_comparator.cpp new file mode 100644 index 00000000..74778288 --- /dev/null +++ b/src/animation/transition_value_boolean_comparator.cpp @@ -0,0 +1,16 @@ +#include "rive/animation/transition_value_boolean_comparator.hpp" + +using namespace rive; + +bool TransitionValueBooleanComparator::compare(TransitionComparator* comparand, + TransitionConditionOp operation, + const StateMachineInstance* stateMachineInstance) +{ + if (comparand->is()) + { + return compareBooleans(value(), + comparand->as()->value(), + operation); + } + return false; +} \ No newline at end of file diff --git a/src/animation/transition_value_color_comparator.cpp b/src/animation/transition_value_color_comparator.cpp new file mode 100644 index 00000000..9e29e26f --- /dev/null +++ b/src/animation/transition_value_color_comparator.cpp @@ -0,0 +1,16 @@ +#include "rive/animation/transition_value_color_comparator.hpp" + +using namespace rive; + +bool TransitionValueColorComparator::compare(TransitionComparator* comparand, + TransitionConditionOp operation, + const StateMachineInstance* stateMachineInstance) +{ + if (comparand->is()) + { + return compareColors(value(), + comparand->as()->value(), + operation); + } + return false; +} \ No newline at end of file diff --git a/src/animation/transition_value_enum_comparator.cpp b/src/animation/transition_value_enum_comparator.cpp new file mode 100644 index 00000000..8742e37d --- /dev/null +++ b/src/animation/transition_value_enum_comparator.cpp @@ -0,0 +1,4 @@ +#include "rive/animation/transition_value_enum_comparator.hpp" +#include "rive/viewmodel/viewmodel_instance_enum.hpp" + +using namespace rive; \ No newline at end of file diff --git a/src/animation/transition_value_number_comparator.cpp b/src/animation/transition_value_number_comparator.cpp new file mode 100644 index 00000000..5c88856a --- /dev/null +++ b/src/animation/transition_value_number_comparator.cpp @@ -0,0 +1,16 @@ +#include "rive/animation/transition_value_number_comparator.hpp" + +using namespace rive; + +bool TransitionValueNumberComparator::compare(TransitionComparator* comparand, + TransitionConditionOp operation, + const StateMachineInstance* stateMachineInstance) +{ + if (comparand->is()) + { + return compareNumbers(value(), + comparand->as()->value(), + operation); + } + return false; +} \ No newline at end of file diff --git a/src/animation/transition_value_string_comparator.cpp b/src/animation/transition_value_string_comparator.cpp new file mode 100644 index 00000000..fe6ac06e --- /dev/null +++ b/src/animation/transition_value_string_comparator.cpp @@ -0,0 +1,16 @@ +#include "rive/animation/transition_value_string_comparator.hpp" + +using namespace rive; + +bool TransitionValueStringComparator::compare(TransitionComparator* comparand, + TransitionConditionOp operation, + const StateMachineInstance* stateMachineInstance) +{ + if (comparand->is()) + { + return compareStrings(value(), + comparand->as()->value(), + operation); + } + return false; +} \ No newline at end of file diff --git a/src/animation/transition_viewmodel_condition.cpp b/src/animation/transition_viewmodel_condition.cpp new file mode 100644 index 00000000..5c6ebbf9 --- /dev/null +++ b/src/animation/transition_viewmodel_condition.cpp @@ -0,0 +1,18 @@ +#include "rive/animation/transition_viewmodel_condition.hpp" +#include "rive/animation/state_transition.hpp" +#include "rive/importers/state_transition_importer.hpp" +#include "rive/importers/state_machine_importer.hpp" +#include "rive/animation/state_machine.hpp" +#include "rive/animation/state_machine_instance.hpp" +#include "rive/component_dirt.hpp" + +using namespace rive; + +bool TransitionViewModelCondition::evaluate(const StateMachineInstance* stateMachineInstance) const +{ + if (leftComparator() != nullptr && rightComparator() != nullptr) + { + return leftComparator()->compare(rightComparator(), op(), stateMachineInstance); + } + return false; +} \ No newline at end of file diff --git a/src/artboard.cpp b/src/artboard.cpp index b386bf77..b153350e 100644 --- a/src/artboard.cpp +++ b/src/artboard.cpp @@ -2,8 +2,11 @@ #include "rive/backboard.hpp" #include "rive/animation/linear_animation_instance.hpp" #include "rive/dependency_sorter.hpp" +#include "rive/data_bind/data_bind.hpp" +#include "rive/data_bind/data_bind_context.hpp" #include "rive/draw_rules.hpp" #include "rive/draw_target.hpp" +#include "rive/audio_event.hpp" #include "rive/draw_target_placement.hpp" #include "rive/drawable.hpp" #include "rive/animation/keyed_object.hpp" @@ -12,19 +15,42 @@ #include "rive/shapes/paint/shape_paint.hpp" #include "rive/importers/import_stack.hpp" #include "rive/importers/backboard_importer.hpp" +#include "rive/layout_component.hpp" #include "rive/nested_artboard.hpp" +#include "rive/nested_artboard_leaf.hpp" +#include "rive/nested_artboard_layout.hpp" #include "rive/joystick.hpp" +#include "rive/data_bind_flags.hpp" +#include "rive/animation/nested_bool.hpp" +#include "rive/animation/nested_number.hpp" +#include "rive/animation/nested_trigger.hpp" +#include "rive/animation/state_machine_input_instance.hpp" #include "rive/animation/state_machine_instance.hpp" #include "rive/shapes/shape.hpp" #include "rive/text/text_value_run.hpp" #include "rive/event.hpp" +#include "rive/assets/audio_asset.hpp" #include using namespace rive; +Artboard::Artboard() {} + Artboard::~Artboard() { +#ifdef WITH_RIVE_AUDIO +#ifdef EXTERNAL_RIVE_AUDIO_ENGINE + auto audioEngine = m_audioEngine; +#else + auto audioEngine = AudioEngine::RuntimeEngine(false); +#endif + if (audioEngine) + { + audioEngine->stop(this); + } +#endif + for (auto object : m_Objects) { // First object is artboard @@ -63,9 +89,14 @@ StatusCode Artboard::initialize() StatusCode code; // these will be re-built in update() -- are they needed here? - m_BackgroundPath = factory()->makeEmptyRenderPath(); - m_ClipPath = factory()->makeEmptyRenderPath(); - + m_backgroundPath = factory()->makeEmptyRenderPath(); + m_clipPath = factory()->makeEmptyRenderPath(); + m_layoutSizeWidth = width(); + m_layoutSizeHeight = height(); + +#ifdef WITH_RIVE_LAYOUT + markLayoutDirty(this); +#endif // onAddedDirty guarantees that all objects are now available so they can be // looked up by index/id. This is where nodes find their parents, but they // can't assume that their parent's parent will have resolved yet. @@ -145,6 +176,8 @@ StatusCode Artboard::initialize() break; } case NestedArtboardBase::typeKey: + case NestedArtboardLeafBase::typeKey: + case NestedArtboardLayoutBase::typeKey: m_NestedArtboards.push_back(object->as()); break; @@ -192,7 +225,7 @@ StatusCode Artboard::initialize() { object->as()->buildDependencies(); } - if (object->is()) + if (object->is() && object != this) { Drawable* drawable = object->as(); m_Drawables.push_back(drawable); @@ -209,6 +242,47 @@ StatusCode Artboard::initialize() } } } + // Iterate over the drawables in order to inject proxies for layouts + std::vector layouts; + for (int i = 0; i < m_Drawables.size(); i++) + { + auto drawable = m_Drawables[i]; + LayoutComponent* currentLayout = nullptr; + bool isInCurrentLayout = true; + if (!layouts.empty()) + { + currentLayout = layouts.back(); + isInCurrentLayout = drawable->isChildOfLayout(currentLayout); + } + // We inject a DrawableProxy after all of the children of a LayoutComponent + // so that we can draw a stroke above and background below the children + // This also allows us to clip the children + if (currentLayout != nullptr && !isInCurrentLayout) + { + // This is the first item in the list of drawables that isn't a child + // of the layout, so we insert a proxy before it + do + { + m_Drawables.insert(m_Drawables.begin() + i, currentLayout->proxy()); + layouts.pop_back(); + if (!layouts.empty()) + { + currentLayout = layouts.back(); + } + i += 1; + } while (!layouts.empty() && !drawable->isChildOfLayout(currentLayout)); + } + if (drawable->is()) + { + layouts.push_back(drawable->as()); + } + } + while (!layouts.empty()) + { + auto layout = layouts.back(); + m_Drawables.push_back(layout->proxy()); + layouts.pop_back(); + } sortDependencies(); @@ -267,6 +341,10 @@ StatusCode Artboard::initialize() m_DrawTargets.push_back(static_cast(*itr++)); } + // Some default layout dimensions. + m_layoutSizeWidth = width(); + m_layoutSizeHeight = height(); + return StatusCode::Ok; } @@ -417,30 +495,134 @@ void Artboard::onComponentDirty(Component* component) void Artboard::onDirty(ComponentDirt dirt) { m_Dirt |= ComponentDirt::Components; } +#ifdef WITH_RIVE_LAYOUT +void Artboard::propagateSize() +{ + addDirt(ComponentDirt::Path); + if (sharesLayoutWithHost()) + { + m_host->markTransformDirty(); + } +#ifdef WITH_RIVE_TOOLS + if (m_layoutChangedCallback != nullptr) + { + m_layoutChangedCallback(this); + } +#endif +} +#endif + +bool Artboard::sharesLayoutWithHost() const +{ + return m_host != nullptr && m_host->is(); +} +void Artboard::host(NestedArtboard* nestedArtboard) +{ + m_host = nestedArtboard; +#ifdef WITH_RIVE_LAYOUT + if (!sharesLayoutWithHost()) + { + return; + } + Artboard* parent = parentArtboard(); + if (parent != nullptr) + { + parent->markLayoutDirty(this); + parent->syncLayoutChildren(); + } +#endif +} + +NestedArtboard* Artboard::host() const { return m_host; } + +Artboard* Artboard::parentArtboard() const +{ + if (m_host == nullptr) + { + return nullptr; + } + return m_host->artboard(); +} + +float Artboard::layoutWidth() const +{ +#ifdef WITH_RIVE_LAYOUT + return m_layoutSizeWidth; +#else + return width(); +#endif +} + +float Artboard::layoutHeight() const +{ +#ifdef WITH_RIVE_LAYOUT + return m_layoutSizeHeight; +#else + return height(); +#endif +} + +float Artboard::layoutX() const +{ +#ifdef WITH_RIVE_LAYOUT + return m_layoutLocationX; +#else + return 0.0f; +#endif +} + +float Artboard::layoutY() const +{ +#ifdef WITH_RIVE_LAYOUT + return m_layoutLocationY; +#else + return 0.0f; +#endif +} + +void Artboard::updateRenderPath() +{ + AABB bg = AABB::fromLTWH(-layoutWidth() * originX(), + -layoutHeight() * originY(), + layoutWidth(), + layoutHeight()); + AABB clip; + if (m_FrameOrigin) + { + clip = {0.0f, 0.0f, layoutWidth(), layoutHeight()}; + } + else + { + clip = bg; + } + m_clipPath = factory()->makeRenderPath(clip); + m_backgroundRawPath.rewind(); + m_backgroundRawPath.addRect(bg); + m_backgroundPath->rewind(); + m_backgroundRawPath.addTo(m_backgroundPath.get()); +} + void Artboard::update(ComponentDirt value) { + Super::update(value); if (hasDirt(value, ComponentDirt::DrawOrder)) { sortDrawOrder(); } - if (hasDirt(value, ComponentDirt::Path)) +} + +void Artboard::updateDataBinds() +{ + for (auto dataBind : m_AllDataBinds) { - AABB bg = AABB::fromLTWH(-width() * originX(), -height() * originY(), width(), height()); - AABB clip; - if (m_FrameOrigin) + dataBind->updateSourceBinding(); + auto d = dataBind->dirt(); + if (d == ComponentDirt::None) { - clip = {0.0f, 0.0f, width(), height()}; - } - else - { - clip = bg; + continue; } - m_ClipPath = factory()->makeRenderPath(clip); - m_BackgroundPath = factory()->makeRenderPath(bg); - } - if (hasDirt(value, ComponentDirt::RenderOpacity)) - { - propagateOpacity(childOpacity()); + dataBind->dirt(ComponentDirt::None); + dataBind->update(d); } } @@ -485,9 +667,101 @@ bool Artboard::updateComponents() return false; } -bool Artboard::advance(double elapsedSeconds) +void* Artboard::takeLayoutNode() +{ +#ifdef WITH_RIVE_LAYOUT + m_updatesOwnLayout = false; + return static_cast(&layoutNode()); +#else + return nullptr; +#endif +} + +void Artboard::markLayoutDirty(LayoutComponent* layoutComponent) +{ +#ifdef WITH_RIVE_TOOLS + if (m_dirtyLayout.empty() && m_layoutDirtyCallback != nullptr) + { + m_layoutDirtyCallback(this); + } +#endif + m_dirtyLayout.insert(layoutComponent); + if (sharesLayoutWithHost()) + { + // TODO: Follow up with Luigi + // This only gets called when the NestedArtboardLayout is in the runtime + // but seems to cause an infinite loop in certain cases + // m_host->as()->markNestedLayoutDirty(); + } +} + +bool Artboard::syncStyleChanges() +{ + bool updated = false; +#ifdef WITH_RIVE_LAYOUT + if (!m_dirtyLayout.empty()) + { + for (auto layout : m_dirtyLayout) + { + switch (layout->coreType()) + { + case ArtboardBase::typeKey: + { + auto artboard = layout->as(); + if (artboard == this) + { + artboard->syncStyle(); + } + else + { + // This is a nested artboard, sync its changes too. + artboard->syncStyleChanges(); + } + break; + } + + default: + layout->syncStyle(); + break; + } + } + m_dirtyLayout.clear(); + updated = true; + } +#endif + return updated; +} + +bool Artboard::advanceInternal(double elapsedSeconds, bool isRoot, bool nested) { + bool didUpdate = false; m_HasChangedDrawOrderInLastUpdate = false; +#ifdef WITH_RIVE_LAYOUT + if (hasDirt(ComponentDirt::LayoutStyle)) + { + cascadeAnimationStyle(interpolation(), interpolator(), interpolationTime()); + } + + if (syncStyleChanges() && m_updatesOwnLayout) + { + calculateLayout(); + } + + for (auto dep : m_DependencyOrder) + { + if (dep->is()) + { + auto layout = dep->as(); + layout->updateLayoutBounds(); + if ((dep == this && Super::advance(elapsedSeconds)) || + (dep != this && layout->advance(elapsedSeconds))) + { + didUpdate = true; + } + } + } + +#endif if (m_JoysticksApplyBeforeUpdate) { for (auto joystick : m_Joysticks) @@ -495,14 +769,24 @@ bool Artboard::advance(double elapsedSeconds) joystick->apply(this); } } - - bool didUpdate = updateComponents(); + if (isRoot) + { + updateDataBinds(); + } + if (updateComponents()) + { + didUpdate = true; + } if (!m_JoysticksApplyBeforeUpdate) { for (auto joystick : m_Joysticks) { if (!joystick->canApplyBeforeUpdate()) { + if (isRoot) + { + updateDataBinds(); + } if (updateComponents()) { didUpdate = true; @@ -510,32 +794,44 @@ bool Artboard::advance(double elapsedSeconds) } joystick->apply(this); } + if (isRoot) + { + updateDataBinds(); + } if (updateComponents()) { didUpdate = true; } } - for (auto nestedArtboard : m_NestedArtboards) + if (nested) { - if (nestedArtboard->advance((float)elapsedSeconds)) + for (auto nestedArtboard : m_NestedArtboards) { - didUpdate = true; + if (nestedArtboard->advance((float)elapsedSeconds)) + { + didUpdate = true; + } } } return didUpdate; } -Core* Artboard::hitTest(HitInfo* hinfo, const Mat2D* xform) +bool Artboard::advance(double elapsedSeconds, bool nested) +{ + return advanceInternal(elapsedSeconds, true, nested); +} + +Core* Artboard::hitTest(HitInfo* hinfo, const Mat2D& xform) { if (clip()) { // TODO: can we get the rawpath for the clip? } - auto mx = xform ? *xform : Mat2D(); + auto mx = xform; if (m_FrameOrigin) { - mx *= Mat2D::fromTranslate(width() * originX(), height() * originY()); + mx *= Mat2D::fromTranslate(layoutWidth() * originX(), layoutHeight() * originY()); } Drawable* last = m_FirstDrawable; @@ -564,19 +860,21 @@ Core* Artboard::hitTest(HitInfo* hinfo, const Mat2D* xform) return nullptr; } +void Artboard::draw(Renderer* renderer) { draw(renderer, DrawOption::kNormal); } + void Artboard::draw(Renderer* renderer, DrawOption option) { renderer->save(); if (clip()) { - renderer->clipPath(m_ClipPath.get()); + renderer->clipPath(m_clipPath.get()); } if (m_FrameOrigin) { Mat2D artboardTransform; - artboardTransform[4] = width() * originX(); - artboardTransform[5] = height() * originY(); + artboardTransform[4] = layoutWidth() * originX(); + artboardTransform[5] = layoutHeight() * originY(); renderer->transform(artboardTransform); } @@ -584,7 +882,7 @@ void Artboard::draw(Renderer* renderer, DrawOption option) { for (auto shapePaint : m_ShapePaints) { - shapePaint->draw(renderer, m_BackgroundPath.get()); + shapePaint->draw(renderer, m_backgroundPath.get(), &m_backgroundRawPath); } } @@ -616,11 +914,19 @@ void Artboard::addToRenderPath(RenderPath* path, const Mat2D& transform) } } +Vec2D Artboard::origin() const +{ + return m_FrameOrigin ? Vec2D(0.0f, 0.0f) + : Vec2D(-layoutWidth() * originX(), -layoutHeight() * originY()); +} + AABB Artboard::bounds() const { - return m_FrameOrigin - ? AABB(0.0f, 0.0f, width(), height()) - : AABB::fromLTWH(-width() * originX(), -height() * originY(), width(), height()); + return m_FrameOrigin ? AABB(0.0f, 0.0f, layoutWidth(), layoutHeight()) + : AABB::fromLTWH(-layoutWidth() * originX(), + -layoutHeight() * originY(), + layoutWidth(), + layoutHeight()); } bool Artboard::isTranslucent() const @@ -635,6 +941,25 @@ bool Artboard::isTranslucent() const return true; } +bool Artboard::hasAudio() const +{ + for (auto object : m_Objects) + { + if (object != nullptr && object->coreType() == AudioEventBase::typeKey) + { + return true; + } + } + for (auto nestedArtboard : m_NestedArtboards) + { + if (nestedArtboard->artboardInstance()->hasAudio()) + { + return true; + } + } + return false; +} + bool Artboard::isTranslucent(const LinearAnimation* anim) const { // For now we're conservative/lazy -- if we see that any of our paints are @@ -725,6 +1050,47 @@ int Artboard::defaultStateMachineIndex() const return index; } +NestedArtboard* Artboard::nestedArtboard(const std::string& name) const +{ + for (auto nested : m_NestedArtboards) + { + if (nested->name() == name) + { + return nested; + } + } + return nullptr; +} + +NestedArtboard* Artboard::nestedArtboardAtPath(const std::string& path) const +{ + // name parameter can be a name or a path to recursively find a nested artboard + std::string delimiter = "/"; + size_t firstDelim = path.find(delimiter); + std::string artboardName = firstDelim == std::string::npos ? path : path.substr(0, firstDelim); + std::string restOfPath = + firstDelim == std::string::npos ? "" : path.substr(firstDelim + 1, path.size()); + + // Find the nested artboard at this level + if (!artboardName.empty()) + { + auto nested = nestedArtboard(artboardName); + if (nested != nullptr) + { + if (restOfPath.empty()) + { + return nested; + } + else + { + auto artboard = nested->artboardInstance(); + return artboard->nestedArtboardAtPath(restOfPath); + } + } + } + return nullptr; +} + // std::unique_ptr Artboard::instance() const // { // std::unique_ptr artboardClone(new ArtboardInstance); @@ -796,6 +1162,112 @@ StatusCode Artboard::import(ImportStack& importStack) return result; } +void Artboard::internalDataContext(DataContext* value, DataContext* parent, bool isRoot) +{ + m_DataContext = value; + m_DataContext->parent(parent); + for (auto nestedArtboard : m_NestedArtboards) + { + if (nestedArtboard->artboardInstance() == nullptr) + { + continue; + } + auto value = m_DataContext->getViewModelInstance(nestedArtboard->dataBindPathIds()); + if (value != nullptr && value->is()) + { + nestedArtboard->dataContextFromInstance(value, m_DataContext); + } + else + { + nestedArtboard->internalDataContext(m_DataContext, m_DataContext->parent()); + } + } + for (auto dataBind : m_DataBinds) + { + if (dataBind->is()) + { + dataBind->as()->bindFromContext(m_DataContext); + } + } + if (isRoot) + { + std::vector dataBinds; + populateDataBinds(&dataBinds); + sortDataBinds(dataBinds); + } +} + +void Artboard::sortDataBinds(std::vector dataBinds) +{ + // TODO: @hernan review this. Should not need to push to a component list to sort. + + for (auto dataBind : dataBinds) + { + m_AllDataBinds.push_back(dataBind->as()); + } +} + +float Artboard::volume() const { return m_volume; } +void Artboard::volume(float value) +{ + m_volume = value; + for (auto nestedArtboard : m_NestedArtboards) + { + auto artboard = nestedArtboard->artboardInstance(); + if (artboard != nullptr) + { + artboard->volume(value); + } + } +} + +void Artboard::populateDataBinds(std::vector* dataBinds) +{ + for (auto dataBind : m_DataBinds) + { + dataBinds->push_back(dataBind); + } + for (auto nestedArtboard : m_NestedArtboards) + { + if (nestedArtboard->artboardInstance() != nullptr) + { + nestedArtboard->artboardInstance()->populateDataBinds(dataBinds); + } + } +} + +void Artboard::addDataBind(DataBind* dataBind) { m_DataBinds.push_back(dataBind); } + +void Artboard::dataContext(DataContext* value, DataContext* parent) +{ + internalDataContext(value, parent, true); +} + +void Artboard::dataContextFromInstance(ViewModelInstance* viewModelInstance) +{ + dataContextFromInstance(viewModelInstance, nullptr, true); +} + +void Artboard::dataContextFromInstance(ViewModelInstance* viewModelInstance, DataContext* parent) +{ + dataContextFromInstance(viewModelInstance, parent, true); +} + +void Artboard::dataContextFromInstance(ViewModelInstance* viewModelInstance, + DataContext* parent, + bool isRoot) +{ + if (viewModelInstance == nullptr) + { + return; + } + if (isRoot) + { + viewModelInstance->setAsRoot(); + } + internalDataContext(new DataContext(viewModelInstance), parent, isRoot); +} + ////////// ArtboardInstance #include "rive/animation/linear_animation_instance.hpp" @@ -849,6 +1321,65 @@ std::unique_ptr ArtboardInstance::defaultScene() return scene; } +SMIInput* ArtboardInstance::input(const std::string& name, const std::string& path) +{ + return getNamedInput(name, path); +} + +template +InstType* ArtboardInstance::getNamedInput(const std::string& name, const std::string& path) +{ + if (!path.empty()) + { + auto nestedArtboard = nestedArtboardAtPath(path); + if (nestedArtboard != nullptr) + { + auto input = nestedArtboard->input(name); + if (input != nullptr && input->input() != nullptr) + { + return static_cast(input->input()); + } + } + } + return nullptr; +} + +SMIBool* ArtboardInstance::getBool(const std::string& name, const std::string& path) +{ + return getNamedInput(name, path); +} + +SMINumber* ArtboardInstance::getNumber(const std::string& name, const std::string& path) +{ + return getNamedInput(name, path); +} +SMITrigger* ArtboardInstance::getTrigger(const std::string& name, const std::string& path) +{ + return getNamedInput(name, path); +} + +TextValueRun* ArtboardInstance::getTextRun(const std::string& name, const std::string& path) +{ + if (path.empty()) + { + return nullptr; + } + + auto nestedArtboard = nestedArtboardAtPath(path); + if (nestedArtboard == nullptr) + { + return nullptr; + } + + auto artboardInstance = nestedArtboard->artboardInstance(); + if (artboardInstance == nullptr) + { + return nullptr; + } + + return artboardInstance->find(name); +} + #ifdef EXTERNAL_RIVE_AUDIO_ENGINE rcp Artboard::audioEngine() const { return m_audioEngine; } void Artboard::audioEngine(rcp audioEngine) @@ -856,7 +1387,7 @@ void Artboard::audioEngine(rcp audioEngine) m_audioEngine = audioEngine; for (auto nestedArtboard : m_NestedArtboards) { - auto artboard = nestedArtboard->artboard(); + auto artboard = nestedArtboard->artboardInstance(); if (artboard != nullptr) { artboard->audioEngine(audioEngine); diff --git a/src/assets/file_asset.cpp b/src/assets/file_asset.cpp index e6205ee5..03a87666 100644 --- a/src/assets/file_asset.cpp +++ b/src/assets/file_asset.cpp @@ -20,19 +20,21 @@ StatusCode FileAsset::import(ImportStack& importStack) return Super::import(importStack); } -std::string FileAsset::uniqueFilename() const +std::string FileAsset::uniqueName() const { // remove final extension - std::string uniqueFilename = name(); - std::size_t finalDot = uniqueFilename.rfind('.'); + std::string uniqueName = name(); + std::size_t finalDot = uniqueName.rfind('.'); if (finalDot != std::string::npos) { - uniqueFilename = uniqueFilename.substr(0, finalDot); + uniqueName = uniqueName.substr(0, finalDot); } - return uniqueFilename + "-" + std::to_string(assetId()) + "." + fileExtension(); + return uniqueName + "-" + std::to_string(assetId()); } +std::string FileAsset::uniqueFilename() const { return uniqueName() + "." + fileExtension(); } + void FileAsset::copyCdnUuid(const FileAssetBase& object) { // Should never be called. diff --git a/src/audio/audio_engine.cpp b/src/audio/audio_engine.cpp index c85eee17..0bdd9f9d 100644 --- a/src/audio/audio_engine.cpp +++ b/src/audio/audio_engine.cpp @@ -18,20 +18,187 @@ #include "rive/audio/audio_sound.hpp" #include "rive/audio/audio_source.hpp" +#include +#include + using namespace rive; void AudioEngine::SoundCompleted(void* pUserData, ma_sound* pSound) { AudioSound* audioSound = (AudioSound*)pUserData; - audioSound->complete(); + auto engine = audioSound->m_engine; + engine->soundCompleted(ref_rcp(audioSound)); +} + +void AudioEngine::unlinkSound(rcp sound) +{ + auto next = sound->m_nextPlaying; + auto prev = sound->m_prevPlaying; + if (next != nullptr) + { + next->m_prevPlaying = prev; + } + if (prev != nullptr) + { + prev->m_nextPlaying = next; + } + + if (m_playingSoundsHead == sound) + { + m_playingSoundsHead = next; + } + + sound->m_nextPlaying = nullptr; + sound->m_prevPlaying = nullptr; +} + +void AudioEngine::soundCompleted(rcp sound) +{ + std::unique_lock lock(m_mutex); + m_completedSounds.push_back(sound); + unlinkSound(sound); +} + +#ifdef WITH_RIVE_AUDIO_TOOLS +namespace rive +{ +class LevelsNode +{ +public: + ma_node_base base; + AudioEngine* engine; + static void measureLevels(ma_node* pNode, + const float** ppFramesIn, + ma_uint32* pFrameCountIn, + float** ppFramesOut, + ma_uint32* pFrameCountOut) + { + const float* frames = ppFramesIn[0]; + + ma_uint32 frameCount = pFrameCountIn[0]; + + static_cast(pNode)->engine->measureLevels(frames, (uint32_t)frameCount); + } +}; +} // namespace rive + +void AudioEngine::measureLevels(const float* frames, uint32_t frameCount) +{ + uint32_t channelCount = channels(); + + for (uint32_t i = 0; i < frameCount; i++) + { + for (uint32_t c = 0; c < channelCount; c++) + { + float sample = *frames++; + m_levels[c] = std::max(m_levels[c], sample); + } + } } +static ma_node_vtable measure_levels_vtable = {LevelsNode::measureLevels, + nullptr, + 1, + 1, + MA_NODE_FLAG_PASSTHROUGH}; + +void AudioEngine::initLevelMonitor() +{ + if (m_levelMonitor == nullptr) + { + m_levelMonitor = new LevelsNode(); + m_levelMonitor->engine = this; + + ma_node_config nodeConfig = ma_node_config_init(); + nodeConfig.vtable = &measure_levels_vtable; + uint32_t channelCount = channels(); + nodeConfig.pInputChannels = &channelCount; + nodeConfig.pOutputChannels = &channelCount; + m_levels.resize(channelCount); + + auto graph = ma_engine_get_node_graph(m_engine); + if (ma_node_init(graph, &nodeConfig, nullptr, &m_levelMonitor->base) != MA_SUCCESS) + { + delete m_levelMonitor; + m_levelMonitor = nullptr; + return; + } + if (ma_node_attach_output_bus(&m_levelMonitor->base, + 0, + ma_node_graph_get_endpoint(graph), + 0) != MA_SUCCESS) + { + ma_node_uninit(&m_levelMonitor->base, nullptr); + delete m_levelMonitor; + m_levelMonitor = nullptr; + return; + } + } +} + +void AudioEngine::levels(Span levels) +{ + int size = std::min((int)m_levels.size(), (int)levels.size()); + for (int i = 0; i < size; i++) + { + levels[i] = m_levels[i]; + m_levels[i] = 0.0f; + } +} + +float AudioEngine::level(uint32_t channel) +{ + if (channel < m_levels.size()) + { + float value = m_levels[channel]; + m_levels[channel] = 0.0f; + return value; + } + return 0.0f; +} +#endif + +void AudioEngine::start() { ma_engine_start(m_engine); } +void AudioEngine::stop() { ma_engine_stop(m_engine); } + rcp AudioEngine::Make(uint32_t numChannels, uint32_t sampleRate) { +// I _think_ MA_NO_DEVICE_IO is defined when building for Unity; otherwise, it seems to pass +// "standard" building When defined, pContext is unavailable, which causes build errors when +// building Unity for iOS. - David +#if (TARGET_IPHONE_SIMULATOR || TARGET_OS_MACCATALYST || TARGET_OS_IPHONE) && \ + !defined(MA_NO_DEVICE_IO) + // Used for configuration only, and isn't referenced past the usage of ma_context_init; thus, + // can be locally scoped. Uses the "logical" defaults from miniaudio, and updates only what we + // need. This should automatically set available backends in priority order based on the target + // it's built for, which in the case of Apple is Core Audio first. + ma_context_config contextConfig = ma_context_config_init(); + contextConfig.coreaudio.sessionCategoryOptions = ma_ios_session_category_option_mix_with_others; + + // We only need to initialize space for the context if we're targeting Apple platforms + ma_context* context = (ma_context*)malloc(sizeof(ma_context)); + + if (ma_context_init(NULL, 0, &contextConfig, context) != MA_SUCCESS) + { + free(context); + context = nullptr; + } +#else + ma_context* context = nullptr; +#endif + ma_engine_config engineConfig = ma_engine_config_init(); engineConfig.channels = numChannels; engineConfig.sampleRate = sampleRate; +#if (TARGET_IPHONE_SIMULATOR || TARGET_OS_MACCATALYST || TARGET_OS_IPHONE) && \ + !defined(MA_NO_DEVICE_IO) + if (context != nullptr) + { + engineConfig.pContext = context; + } +#endif + #ifdef EXTERNAL_RIVE_AUDIO_ENGINE engineConfig.noDevice = MA_TRUE; #endif @@ -40,40 +207,70 @@ rcp AudioEngine::Make(uint32_t numChannels, uint32_t sampleRate) if (ma_engine_init(&engineConfig, engine) != MA_SUCCESS) { +#if (TARGET_IPHONE_SIMULATOR || TARGET_OS_MACCATALYST || TARGET_OS_IPHONE) && \ + !defined(MA_NO_DEVICE_IO) + if (context != nullptr) + { + ma_context_uninit(context); + free(context); + context = nullptr; + } +#endif fprintf(stderr, "AudioEngine::Make - failed to init engine\n"); delete engine; return nullptr; } - return rcp(new AudioEngine(engine)); + return rcp(new AudioEngine(engine, context)); } uint32_t AudioEngine::channels() const { return ma_engine_get_channels(m_engine); } uint32_t AudioEngine::sampleRate() const { return ma_engine_get_sample_rate(m_engine); } -AudioEngine::AudioEngine(ma_engine* engine) : - m_device(ma_engine_get_device(engine)), m_engine(engine) +AudioEngine::AudioEngine(ma_engine* engine, ma_context* context) : + m_device(ma_engine_get_device(engine)), m_engine(engine), m_context(context) {} rcp AudioEngine::play(rcp source, uint64_t startTime, uint64_t endTime, - uint64_t soundStartTime) + uint64_t soundStartTime, + Artboard* artboard) { - purgeCompletedSounds(); + if (endTime != 0 && startTime >= endTime) + { + // Requested to stop sound before start. + return nullptr; + } + + std::unique_lock lock(m_mutex); + // We have to dispose completed sounds out of the completed callback. So we + // do it on next play or at destruct. + for (auto sound : m_completedSounds) + { + sound->dispose(); + } + m_completedSounds.clear(); - rive::rcp rcEngine = rive::rcp(this); - rcEngine->ref(); - rcp audioSound = rcp(new AudioSound(rcEngine)); + rcp audioSound = rcp(new AudioSound(this, source, artboard)); if (source->isBuffered()) { rive::Span samples = source->bufferedSamples(); - ma_audio_buffer_config config = - ma_audio_buffer_config_init(ma_format_f32, - source->channels(), - samples.size() / source->channels(), - (const void*)samples.data(), - nullptr); + ma_uint64 sizeInFrames = samples.size() / source->channels(); + if (endTime != 0) + { + float durationSeconds = (soundStartTime + endTime - startTime) / (float)sampleRate(); + ma_uint64 clippedFrames = (ma_uint64)std::round(durationSeconds * source->sampleRate()); + if (clippedFrames < sizeInFrames) + { + sizeInFrames = clippedFrames; + } + } + ma_audio_buffer_config config = ma_audio_buffer_config_init(ma_format_f32, + source->channels(), + sizeInFrames, + (const void*)samples.data(), + nullptr); if (ma_audio_buffer_init(&config, audioSound->buffer()) != MA_SUCCESS) { fprintf(stderr, "AudioSource::play - Failed to initialize audio buffer.\n"); @@ -90,18 +287,34 @@ rcp AudioEngine::play(rcp source, } else { + // We wrapped the miniaudio decoder with a custom data source "Clipped + // Decoder" which lets us ensure that the end callback for the sound is + // called when we reach the end of the clip. This won't happen when + // using ma_sound_set_stop_time_in_pcm_frames(audioSound->sound(), + // endTime); as this keeps the sound playing/ready to fade back in. + auto clip = audioSound->clippedDecoder(); ma_decoder_config config = ma_decoder_config_init(ma_format_f32, channels(), sampleRate()); auto sourceBytes = source->bytes(); if (ma_decoder_init_memory(sourceBytes.data(), sourceBytes.size(), &config, - audioSound->decoder()) != MA_SUCCESS) + &clip->decoder) != MA_SUCCESS) { fprintf(stderr, "AudioSource::play - Failed to initialize decoder.\n"); return nullptr; } + clip->frameCursor = 0; + clip->endFrame = endTime == 0 ? std::numeric_limits::max() + : soundStartTime + endTime - startTime; + ma_data_source_config baseConfig = ma_data_source_config_init(); + baseConfig.vtable = &g_ma_end_clipped_decoder_vtable; + if (ma_data_source_init(&baseConfig, &clip->base) != MA_SUCCESS) + { + return nullptr; + } + if (ma_sound_init_from_data_source(m_engine, - &audioSound->m_decoder, + audioSound->clippedDecoder(), MA_SOUND_FLAG_NO_PITCH | MA_SOUND_FLAG_NO_SPATIALIZATION, nullptr, audioSound->sound()) != MA_SUCCESS) @@ -115,43 +328,108 @@ rcp AudioEngine::play(rcp source, audioSound->seek(soundStartTime); } - // one extra ref for sound as we're waiting for playback to complete. - audioSound->ref(); - ma_sound_set_end_callback(audioSound->sound(), SoundCompleted, audioSound.get()); if (startTime != 0) { ma_sound_set_start_time_in_pcm_frames(audioSound->sound(), startTime); } - if (endTime != 0) +#ifdef WITH_RIVE_AUDIO_TOOLS + if (m_levelMonitor != nullptr) { - ma_sound_set_stop_time_in_pcm_frames(audioSound->sound(), endTime); + ma_node_attach_output_bus(audioSound->sound(), 0, m_levelMonitor, 0); } +#endif if (ma_sound_start(audioSound->sound()) != MA_SUCCESS) { fprintf(stderr, "AudioSource::play - failed to start sound\n"); return nullptr; } + if (m_playingSoundsHead != nullptr) + { + m_playingSoundsHead->m_prevPlaying = audioSound; + } + audioSound->m_nextPlaying = m_playingSoundsHead; + m_playingSoundsHead = audioSound; + return audioSound; } -void AudioEngine::completeSound(rcp sound) { m_completedSounds.push_back(sound); } +#ifdef TESTING +size_t AudioEngine::playingSoundCount() +{ + std::unique_lock lock(m_mutex); + size_t count = 0; + auto sound = m_playingSoundsHead; + while (sound != nullptr) + { + count++; + sound = sound->m_nextPlaying; + } -void AudioEngine::purgeCompletedSounds() + return count; +} +#endif + +void AudioEngine::stop(Artboard* artboard) { - for (auto sound : m_completedSounds) + std::unique_lock lock(m_mutex); + auto sound = m_playingSoundsHead; + while (sound != nullptr) { - sound->unref(); + auto next = sound->m_nextPlaying; + if (sound->m_artboard == artboard) + { + sound->stop(); + m_completedSounds.push_back(sound); + unlinkSound(sound); + } + sound = next; } - m_completedSounds.clear(); } AudioEngine::~AudioEngine() { + auto sound = m_playingSoundsHead; + while (sound != nullptr) + { + sound->dispose(); + + auto next = sound->m_nextPlaying; + sound->m_nextPlaying = nullptr; + sound->m_prevPlaying = nullptr; + sound = next; + } + + for (auto sound : m_completedSounds) + { + sound->dispose(); + } + m_completedSounds.clear(); + +#if (TARGET_IPHONE_SIMULATOR || TARGET_OS_MACCATALYST || TARGET_OS_IPHONE) && \ + !defined(MA_NO_DEVICE_IO) + // m_context is only set when Core Audio is available + if (m_context != nullptr) + { + ma_context_uninit(m_context); + free(m_context); + m_context = nullptr; + } +#endif + ma_engine_uninit(m_engine); delete m_engine; + +#ifdef WITH_RIVE_AUDIO_TOOLS + if (m_levelMonitor != nullptr) + { + ma_node_uninit(&m_levelMonitor->base, nullptr); + delete m_levelMonitor; + } + +#endif } uint64_t AudioEngine::timeInFrames() @@ -159,10 +437,18 @@ uint64_t AudioEngine::timeInFrames() return (uint64_t)ma_engine_get_time_in_pcm_frames(m_engine); } -rcp AudioEngine::RuntimeEngine() +static rcp m_runtimeAudioEngine; +rcp AudioEngine::RuntimeEngine(bool makeWhenNecessary) { - static rcp engine = AudioEngine::Make(defaultNumChannels, defaultSampleRate); - return engine; + if (!makeWhenNecessary) + { + return m_runtimeAudioEngine; + } + else if (m_runtimeAudioEngine == nullptr) + { + m_runtimeAudioEngine = AudioEngine::Make(defaultNumChannels, defaultSampleRate); + } + return m_runtimeAudioEngine; } #ifdef EXTERNAL_RIVE_AUDIO_ENGINE diff --git a/src/audio/audio_reader.cpp b/src/audio/audio_reader.cpp index bfc3d182..8451b3ca 100644 --- a/src/audio/audio_reader.cpp +++ b/src/audio/audio_reader.cpp @@ -12,7 +12,7 @@ AudioReader::AudioReader(rcp audioSource, uint32_t channels) : AudioReader::~AudioReader() { ma_decoder_uninit(&m_decoder); } uint32_t AudioReader::channels() const { return m_channels; } -uint32_t AudioReader::sampleRate() const { return m_source->sampleRate(); } +uint32_t AudioReader::sampleRate() const { return m_decoder.outputSampleRate; } ma_decoder* AudioReader::decoder() { return &m_decoder; } uint64_t AudioReader::lengthInFrames() diff --git a/src/audio/audio_sound.cpp b/src/audio/audio_sound.cpp index 21631f40..052d1785 100644 --- a/src/audio/audio_sound.cpp +++ b/src/audio/audio_sound.cpp @@ -6,19 +6,48 @@ using namespace rive; -AudioSound::AudioSound(rcp engine) : - m_engine(std::move(engine)), m_decoder({}), m_buffer({}), m_sound({}) +AudioSound::AudioSound(AudioEngine* engine, rcp source, Artboard* artboard) : + m_decoder({}), + m_buffer({}), + m_sound({}), + m_source(std::move(source)), + m_isDisposed(false), + m_engine(engine), + m_artboard(artboard) {} -AudioSound::~AudioSound() +void AudioSound::dispose() { + if (m_isDisposed) + { + return; + } + m_isDisposed = true; ma_sound_uninit(&m_sound); - ma_decoder_uninit(&m_decoder); + ma_decoder_uninit(&m_decoder.decoder); ma_audio_buffer_uninit(&m_buffer); } +float AudioSound::volume() { return ma_sound_get_volume(&m_sound); } +void AudioSound::volume(float value) { ma_sound_set_volume(&m_sound, value); } + +bool AudioSound::completed() const +{ + if (m_isDisposed) + { + return true; + } + return (bool)ma_sound_at_end(&m_sound); +} + +AudioSound::~AudioSound() { dispose(); } + void AudioSound::stop(uint64_t fadeTimeInFrames) { + if (m_isDisposed) + { + return; + } if (fadeTimeInFrames == 0) { ma_sound_stop(&m_sound); @@ -29,15 +58,12 @@ void AudioSound::stop(uint64_t fadeTimeInFrames) } } -void AudioSound::complete() -{ - auto sound = rcp(this); - sound->ref(); - m_engine->completeSound(sound); -} - bool AudioSound::seek(uint64_t timeInFrames) { + if (m_isDisposed) + { + return false; + } return ma_sound_seek_to_pcm_frame(&m_sound, (ma_uint64)timeInFrames) == MA_SUCCESS; } diff --git a/src/audio/audio_source.cpp b/src/audio/audio_source.cpp index 8cc5b78e..8701d9ce 100644 --- a/src/audio/audio_source.cpp +++ b/src/audio/audio_source.cpp @@ -1,12 +1,14 @@ -#ifdef WITH_RIVE_AUDIO #include "rive/audio/audio_source.hpp" +#ifdef WITH_RIVE_AUDIO #include "rive/audio/audio_engine.hpp" #include "rive/audio/audio_sound.hpp" #include "rive/audio/audio_reader.hpp" #include "rive/audio/audio_reader.hpp" +#endif using namespace rive; +#ifdef WITH_RIVE_AUDIO AudioSource::AudioSource(rive::Span samples, uint32_t numChannels, uint32_t sampleRate) : m_isBuffered(true), m_channels(numChannels), @@ -146,5 +148,15 @@ rcp AudioSource::makeReader(uint32_t numChannels, uint32_t sampleRa return reader; } - +#else +AudioSource::AudioSource(rive::Span fileBytes) {} +AudioSource::AudioSource(rive::SimpleArray fileBytes) {} +AudioSource::AudioSource(rive::Span samples, uint32_t numChannels, uint32_t sampleRate) {} +uint32_t AudioSource::channels() { return 0; } +uint32_t AudioSource::sampleRate() { return 0; } +AudioFormat AudioSource::format() const { return AudioFormat::unknown; } +const rive::Span AudioSource::bufferedSamples() const +{ + return rive::Span(nullptr, 0); +} #endif \ No newline at end of file diff --git a/src/audio_event.cpp b/src/audio_event.cpp index 8ea32c67..53c98123 100644 --- a/src/audio_event.cpp +++ b/src/audio_event.cpp @@ -6,10 +6,8 @@ using namespace rive; -void AudioEvent::trigger(const CallbackData& value) +void AudioEvent::play() { - Super::trigger(value); - #ifdef WITH_RIVE_AUDIO auto audioAsset = (AudioAsset*)m_fileAsset; if (audioAsset == nullptr) @@ -22,16 +20,37 @@ void AudioEvent::trigger(const CallbackData& value) return; } + auto volume = audioAsset->volume() * artboard()->volume(); + if (volume <= 0.0f) + { + return; + } + auto engine = #ifdef EXTERNAL_RIVE_AUDIO_ENGINE artboard()->audioEngine() != nullptr ? artboard()->audioEngine() : #endif AudioEngine::RuntimeEngine(); - engine->play(audioSource, engine->timeInFrames(), 0, 0); + auto sound = engine->play(audioSource, engine->timeInFrames(), 0, 0, artboard()); + + if (volume != 1.0f) + { + sound->volume(volume); + } #endif } +void AudioEvent::trigger(const CallbackData& value) +{ + Super::trigger(value); + if (!value.context()->playsAudio()) + { + // Context won't play audio, we'll do it ourselves. + play(); + } +} + StatusCode AudioEvent::import(ImportStack& importStack) { auto result = registerReferencer(importStack); diff --git a/src/bones/skin.cpp b/src/bones/skin.cpp index 33a27c68..20418768 100644 --- a/src/bones/skin.cpp +++ b/src/bones/skin.cpp @@ -85,4 +85,10 @@ void Skin::deform(Span vertices) } void Skin::addTendon(Tendon* tendon) { m_Tendons.push_back(tendon); } -void Skin::onDirty(ComponentDirt dirt) { m_Skinnable->markSkinDirty(); } +void Skin::onDirty(ComponentDirt dirt) +{ + if (m_Skinnable != nullptr) + { + m_Skinnable->markSkinDirty(); + } +} diff --git a/src/component.cpp b/src/component.cpp index b68ffdfd..8765b9e6 100644 --- a/src/component.cpp +++ b/src/component.cpp @@ -11,6 +11,7 @@ using namespace rive; StatusCode Component::onAddedDirty(CoreContext* context) { m_Artboard = static_cast(context); + m_DependencyHelper.dependecyRoot(m_Artboard); if (this == m_Artboard) { // We're the artboard, don't parent to ourselves. @@ -26,15 +27,7 @@ StatusCode Component::onAddedDirty(CoreContext* context) return StatusCode::Ok; } -void Component::addDependent(Component* component) -{ - // Make it's not already a dependent. - if (std::find(m_Dependents.begin(), m_Dependents.end(), component) != m_Dependents.end()) - { - return; - } - m_Dependents.push_back(component); -} +void Component::addDependent(Component* component) { m_DependencyHelper.addDependent(component); } bool Component::addDirt(ComponentDirt value, bool recurse) { @@ -49,17 +42,14 @@ bool Component::addDirt(ComponentDirt value, bool recurse) onDirty(m_Dirt); - m_Artboard->onComponentDirty(this); + m_DependencyHelper.onComponentDirty(this); if (!recurse) { return true; } - for (auto d : m_Dependents) - { - d->addDirt(value, true); - } + m_DependencyHelper.addDirt(value); return true; } @@ -97,6 +87,6 @@ bool Component::collapse(bool value) m_Dirt &= ~ComponentDirt::Collapsed; } onDirty(m_Dirt); - m_Artboard->onComponentDirty(this); + m_DependencyHelper.onComponentDirty(this); return true; } \ No newline at end of file diff --git a/src/constraints/distance_constraint.cpp b/src/constraints/distance_constraint.cpp index 7565ed7e..3ea5e664 100644 --- a/src/constraints/distance_constraint.cpp +++ b/src/constraints/distance_constraint.cpp @@ -55,3 +55,7 @@ void DistanceConstraint::constrain(TransformComponent* component) world[4] = position.x; world[5] = position.y; } + +void DistanceConstraint::distanceChanged() { markConstraintDirty(); } + +void DistanceConstraint::modeValueChanged() { markConstraintDirty(); } \ No newline at end of file diff --git a/src/constraints/follow_path_constraint.cpp b/src/constraints/follow_path_constraint.cpp index fa3bb212..8c74d9a5 100644 --- a/src/constraints/follow_path_constraint.cpp +++ b/src/constraints/follow_path_constraint.cpp @@ -5,7 +5,6 @@ #include "rive/math/contour_measure.hpp" #include "rive/math/mat2d.hpp" #include "rive/math/math_types.hpp" -#include "rive/shapes/metrics_path.hpp" #include "rive/shapes/path.hpp" #include "rive/shapes/shape.hpp" #include "rive/transform_component.hpp" @@ -150,11 +149,10 @@ void FollowPathConstraint::update(ComponentDirt value) m_contours.clear(); for (auto path : paths) { - auto commandPath = static_cast(path->commandPath()); - commandPath->addToRawPath(m_rawPath, path->pathTransform()); + m_rawPath.addPath(path->rawPath(), &path->pathTransform()); } - auto measure = ContourMeasureIter(m_rawPath); + auto measure = ContourMeasureIter(&m_rawPath); for (auto contour = measure.next(); contour != nullptr; contour = measure.next()) { m_contours.push_back(contour); @@ -169,12 +167,12 @@ StatusCode FollowPathConstraint::onAddedClean(CoreContext* context) if (m_Target->is()) { Shape* shape = static_cast(m_Target); - shape->addDefaultPathSpace(PathSpace::FollowPath); + shape->addFlags(PathFlags::followPath); } else if (m_Target->is()) { Path* path = static_cast(m_Target); - path->addDefaultPathSpace(PathSpace::FollowPath); + path->addFlags(PathFlags::followPath); } } return Super::onAddedClean(context); diff --git a/src/container_component.cpp b/src/container_component.cpp index 0a252609..2ed1bfe1 100644 --- a/src/container_component.cpp +++ b/src/container_component.cpp @@ -14,4 +14,31 @@ bool ContainerComponent::collapse(bool value) child->collapse(value); } return true; +} + +bool ContainerComponent::forAll(std::function predicate) +{ + if (!predicate(this)) + { + return false; + } + forEachChild(predicate); + return true; +} + +bool ContainerComponent::forEachChild(std::function predicate) +{ + for (Component* child : m_children) + { + if (!predicate(child)) + { + return false; + } + if (child->is() && + !child->as()->forEachChild(predicate)) + { + return false; + } + } + return true; } \ No newline at end of file diff --git a/src/core/binary_data_reader.cpp b/src/core/binary_data_reader.cpp new file mode 100644 index 00000000..1fa7f33a --- /dev/null +++ b/src/core/binary_data_reader.cpp @@ -0,0 +1,102 @@ +#include "rive/core/binary_data_reader.hpp" +#include "rive/core/reader.h" + +using namespace rive; + +BinaryDataReader::BinaryDataReader(uint8_t* bytes, size_t length) : + m_Position(bytes), m_End(bytes + length), m_Overflowed(false), m_Length(length) +{} + +size_t BinaryDataReader::lengthInBytes() const { return m_Length; } + +bool BinaryDataReader::didOverflow() const { return m_Overflowed; } + +void BinaryDataReader::overflow() +{ + m_Overflowed = true; + m_Position = m_End; +} + +uint64_t BinaryDataReader::readVarUint() +{ + uint64_t value; + auto readBytes = decode_uint_leb(m_Position, m_End, &value); + if (readBytes == 0) + { + overflow(); + return 0; + } + m_Position += readBytes; + return value; +} + +uint32_t BinaryDataReader::readVarUint32() +{ + uint32_t value; + auto readBytes = decode_uint_leb32(m_Position, m_End, &value); + if (readBytes == 0) + { + overflow(); + return 0; + } + m_Position += readBytes; + return value; +} + +double BinaryDataReader::readFloat64() +{ + double value; + auto readBytes = decode_double(m_Position, m_End, &value); + if (readBytes == 0) + { + overflow(); + return 0.0; + } + m_Position += readBytes; + return value; +} + +float BinaryDataReader::readFloat32() +{ + float value; + auto readBytes = decode_float(m_Position, m_End, &value); + if (readBytes == 0) + { + overflow(); + return 0.0f; + } + m_Position += readBytes; + return value; +} + +uint8_t BinaryDataReader::readByte() +{ + if (m_End - m_Position < 1) + { + overflow(); + return 0; + } + return *m_Position++; +} + +uint32_t BinaryDataReader::readUint32() +{ + uint32_t value; + auto readBytes = decode_uint_32(m_Position, m_End, &value); + if (readBytes == 0) + { + overflow(); + return 0; + } + m_Position += readBytes; + return value; +} + +void BinaryDataReader::complete(uint8_t* bytes, size_t length) +{ + m_Position = bytes; + m_End = bytes + length; + m_Length = length; +} + +void BinaryDataReader::reset(uint8_t* bytes) { m_Position = bytes; } diff --git a/src/core/binary_reader.cpp b/src/core/binary_reader.cpp index d00413f3..ee3ca01c 100644 --- a/src/core/binary_reader.cpp +++ b/src/core/binary_reader.cpp @@ -113,3 +113,5 @@ uint32_t BinaryReader::readUint32() m_Position += readBytes; return value; } + +void BinaryReader::reset() { m_Position = m_Bytes.begin(); } diff --git a/src/core/binary_writer.cpp b/src/core/binary_writer.cpp new file mode 100644 index 00000000..323e5862 --- /dev/null +++ b/src/core/binary_writer.cpp @@ -0,0 +1,124 @@ +#include "rive/core/binary_writer.hpp" +#include "rive/core/binary_stream.hpp" +#include "rive/core/reader.h" +#include + +using namespace rive; + +BinaryWriter::BinaryWriter(BinaryStream* stream) : m_Stream(stream) {} +BinaryWriter::~BinaryWriter() { m_Stream->flush(); } + +void BinaryWriter::write(float value) +{ + auto bytes = reinterpret_cast(&value); + if (is_big_endian()) + { + uint8_t backwards[4] = {bytes[3], bytes[2], bytes[1], bytes[0]}; + m_Stream->write(backwards, 4); + } + else + { + m_Stream->write(bytes, 4); + } +} + +void BinaryWriter::writeFloat(float value) +{ + auto bytes = reinterpret_cast(&value); + if (is_big_endian()) + { + uint8_t backwards[4] = {bytes[3], bytes[2], bytes[1], bytes[0]}; + m_Stream->write(backwards, 4); + } + else + { + m_Stream->write(bytes, 4); + } +} + +void BinaryWriter::write(double value) +{ + auto bytes = reinterpret_cast(&value); + if (is_big_endian()) + { + uint8_t backwards[8] = + {bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]}; + m_Stream->write(backwards, 8); + } + else + { + m_Stream->write(bytes, 8); + } +} + +void BinaryWriter::writeVarUint(uint64_t value) +{ + uint8_t buffer[16]; + int index = 0; + do + { + uint8_t byte = value & 0x7f; + value >>= 7; + + if (value != 0) + { + // more bytes follow + byte |= 0x80; + } + + buffer[index++] = byte; + } while (value != 0); + m_Stream->write(buffer, index); +} + +void BinaryWriter::writeVarUint(uint32_t value) +{ + uint8_t buffer[16]; + int index = 0; + do + { + uint8_t byte = value & 0x7f; + value >>= 7; + + if (value != 0) + { + // more bytes follow + byte |= 0x80; + } + + buffer[index++] = byte; + } while (value != 0); + m_Stream->write(buffer, index); +} + +void BinaryWriter::write(const uint8_t* bytes, size_t length) +{ + if (length == 0) + { + return; + } + m_Stream->write(bytes, length); +} + +void BinaryWriter::writeDouble(double value) +{ + auto bytes = reinterpret_cast(&value); + if (is_big_endian()) + { + uint8_t backwards[8] = + {bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]}; + m_Stream->write(backwards, 8); + } + else + { + m_Stream->write(bytes, 8); + } +} + +void BinaryWriter::write(uint8_t value) { m_Stream->write((const uint8_t*)&value, 1); } + +void BinaryWriter::write(uint16_t value) { m_Stream->write((const uint8_t*)&value, 2); } + +void BinaryWriter::write(uint32_t value) { m_Stream->write((const uint8_t*)&value, 4); } + +void BinaryWriter::clear() { m_Stream->clear(); } \ No newline at end of file diff --git a/src/data_bind/context/context_value.cpp b/src/data_bind/context/context_value.cpp new file mode 100644 index 00000000..5a129540 --- /dev/null +++ b/src/data_bind/context/context_value.cpp @@ -0,0 +1,122 @@ +#include "rive/data_bind/context/context_value.hpp" +#include "rive/data_bind/context/context_value_color.hpp" +#include "rive/data_bind/data_values/data_type.hpp" +#include "rive/data_bind/data_values/data_value.hpp" +#include "rive/data_bind/data_values/data_value_number.hpp" +#include "rive/data_bind/data_values/data_value_string.hpp" +#include "rive/data_bind/data_values/data_value_enum.hpp" +#include "rive/data_bind/data_values/data_value_color.hpp" +#include "rive/data_bind/data_values/data_value_boolean.hpp" +#include "rive/generated/core_registry.hpp" + +using namespace rive; + +DataBindContextValue::DataBindContextValue(ViewModelInstanceValue* source, + DataConverter* converter) : + m_source(source), m_converter(converter) +{ + if (m_source != nullptr) + { + switch (m_source->coreType()) + { + case ViewModelInstanceNumberBase::typeKey: + m_dataValue = + new DataValueNumber(m_source->as()->propertyValue()); + break; + case ViewModelInstanceStringBase::typeKey: + m_dataValue = + new DataValueString(m_source->as()->propertyValue()); + break; + case ViewModelInstanceColorBase::typeKey: + m_dataValue = + new DataValueColor(m_source->as()->propertyValue()); + break; + case ViewModelInstanceBooleanBase::typeKey: + m_dataValue = + new DataValueBoolean(m_source->as()->propertyValue()); + break; + case ViewModelInstanceEnumBase::typeKey: + { + auto viewmodelInstanceEnum = m_source->as(); + auto viewModelPropertyEnum = + viewmodelInstanceEnum->viewModelProperty()->as(); + m_dataValue = new DataValueEnum(viewmodelInstanceEnum->propertyValue(), + viewModelPropertyEnum->dataEnum()); + } + break; + default: + m_dataValue = new DataValue(); + } + } +} + +void DataBindContextValue::updateSourceValue() +{ + if (m_source != nullptr) + { + switch (m_source->coreType()) + { + case ViewModelInstanceNumberBase::typeKey: + m_dataValue->as()->value( + m_source->as()->propertyValue()); + break; + case ViewModelInstanceStringBase::typeKey: + m_dataValue->as()->value( + m_source->as()->propertyValue()); + break; + case ViewModelInstanceColorBase::typeKey: + m_dataValue->as()->value( + m_source->as()->propertyValue()); + break; + case ViewModelInstanceBooleanBase::typeKey: + m_dataValue->as()->value( + m_source->as()->propertyValue()); + break; + case ViewModelInstanceEnumBase::typeKey: + m_dataValue->as()->value( + m_source->as()->propertyValue()); + break; + } + } +} + +void DataBindContextValue::applyToSource(Core* component, + uint32_t propertyKey, + bool isMainDirection) +{ + auto targetValue = getTargetValue(component, propertyKey); + switch (m_source->coreType()) + { + case ViewModelInstanceNumberBase::typeKey: + { + + auto value = calculateValue(targetValue, isMainDirection); + m_source->as()->propertyValue(value); + } + break; + case ViewModelInstanceStringBase::typeKey: + { + auto value = calculateValue(targetValue, isMainDirection); + m_source->as()->propertyValue(value); + } + break; + case ViewModelInstanceColorBase::typeKey: + { + auto value = calculateValue(targetValue, isMainDirection); + m_source->as()->propertyValue(value); + } + break; + case ViewModelInstanceBooleanBase::typeKey: + { + auto value = calculateValue(targetValue, isMainDirection); + m_source->as()->propertyValue(value); + } + break; + case ViewModelInstanceEnumBase::typeKey: + { + auto value = calculateValue(targetValue, isMainDirection); + m_source->as()->propertyValue(value); + } + break; + } +} \ No newline at end of file diff --git a/src/data_bind/context/context_value_boolean.cpp b/src/data_bind/context/context_value_boolean.cpp new file mode 100644 index 00000000..b08b8f5a --- /dev/null +++ b/src/data_bind/context/context_value_boolean.cpp @@ -0,0 +1,23 @@ +#include "rive/data_bind/context/context_value_boolean.hpp" +#include "rive/data_bind/data_values/data_value_boolean.hpp" +#include "rive/generated/core_registry.hpp" + +using namespace rive; + +DataBindContextValueBoolean::DataBindContextValueBoolean(ViewModelInstanceValue* source, + DataConverter* converter) : + DataBindContextValue(source, converter) +{} + +void DataBindContextValueBoolean::apply(Core* target, uint32_t propertyKey, bool isMainDirection) +{ + updateSourceValue(); + auto value = calculateValue(m_dataValue, isMainDirection); + CoreRegistry::setBool(target, propertyKey, value); +} + +DataValue* DataBindContextValueBoolean::getTargetValue(Core* target, uint32_t propertyKey) +{ + auto value = CoreRegistry::getBool(target, propertyKey); + return new DataValueBoolean(value); +} \ No newline at end of file diff --git a/src/data_bind/context/context_value_color.cpp b/src/data_bind/context/context_value_color.cpp new file mode 100644 index 00000000..c66171dd --- /dev/null +++ b/src/data_bind/context/context_value_color.cpp @@ -0,0 +1,23 @@ +#include "rive/data_bind/context/context_value_color.hpp" +#include "rive/data_bind/data_values/data_value_color.hpp" +#include "rive/generated/core_registry.hpp" + +using namespace rive; + +DataBindContextValueColor::DataBindContextValueColor(ViewModelInstanceValue* source, + DataConverter* converter) : + DataBindContextValue(source, converter) +{} + +void DataBindContextValueColor::apply(Core* target, uint32_t propertyKey, bool isMainDirection) +{ + updateSourceValue(); + auto value = calculateValue(m_dataValue, isMainDirection); + CoreRegistry::setColor(target, propertyKey, value); +} + +DataValue* DataBindContextValueColor::getTargetValue(Core* target, uint32_t propertyKey) +{ + auto value = CoreRegistry::getColor(target, propertyKey); + return new DataValueColor(value); +} \ No newline at end of file diff --git a/src/data_bind/context/context_value_enum.cpp b/src/data_bind/context/context_value_enum.cpp new file mode 100644 index 00000000..dac59203 --- /dev/null +++ b/src/data_bind/context/context_value_enum.cpp @@ -0,0 +1,27 @@ +#include "rive/data_bind/context/context_value_enum.hpp" +#include "rive/data_bind/data_values/data_value_enum.hpp" +#include "rive/generated/core_registry.hpp" + +using namespace rive; + +DataBindContextValueEnum::DataBindContextValueEnum(ViewModelInstanceValue* source, + DataConverter* converter) : + DataBindContextValue(source, converter) +{} + +void DataBindContextValueEnum::apply(Core* target, uint32_t propertyKey, bool isMainDirection) +{ + + updateSourceValue(); + auto value = calculateValue(m_dataValue, isMainDirection); + CoreRegistry::setUint(target, propertyKey, value); +} + +DataValue* DataBindContextValueEnum::getTargetValue(Core* target, uint32_t propertyKey) +{ + auto value = CoreRegistry::getUint(target, propertyKey); + auto viewmodelInstanceEnum = m_source->as(); + auto viewModelPropertyEnum = + viewmodelInstanceEnum->viewModelProperty()->as(); + return new DataValueEnum(value, viewModelPropertyEnum->dataEnum()); +} \ No newline at end of file diff --git a/src/data_bind/context/context_value_list.cpp b/src/data_bind/context/context_value_list.cpp new file mode 100644 index 00000000..92fc2fd0 --- /dev/null +++ b/src/data_bind/context/context_value_list.cpp @@ -0,0 +1,135 @@ +#include "rive/data_bind/context/context_value_list.hpp" +#include "rive/data_bind/context/context_value_list_item.hpp" +#include "rive/generated/core_registry.hpp" +#include "rive/node.hpp" + +using namespace rive; + +DataBindContextValueList::DataBindContextValueList(ViewModelInstanceValue* source, + DataConverter* converter) : + DataBindContextValue(source, converter) +{} + +std::unique_ptr DataBindContextValueList::createArtboard( + Component* target, + Artboard* artboard, + ViewModelInstanceListItem* listItem) const +{ + if (artboard != nullptr) + { + + auto mainArtboard = target->artboard(); + auto dataContext = mainArtboard->dataContext(); + auto artboardCopy = artboard->instance(); + artboardCopy->advanceInternal(0.0f, false); + artboardCopy->dataContextFromInstance(listItem->viewModelInstance(), dataContext, false); + return artboardCopy; + } + return nullptr; +} + +std::unique_ptr DataBindContextValueList::createStateMachineInstance( + ArtboardInstance* artboard) +{ + if (artboard != nullptr) + { + auto stateMachineInstance = artboard->stateMachineAt(0); + stateMachineInstance->advance(0.0f); + return stateMachineInstance; + } + return nullptr; +} + +void DataBindContextValueList::insertItem(Core* target, + ViewModelInstanceListItem* listItem, + int index) +{ + auto artboard = listItem->artboard(); + auto artboardCopy = createArtboard(target->as(), artboard, listItem); + auto stateMachineInstance = createStateMachineInstance(artboardCopy.get()); + std::unique_ptr cacheListItem = + rivestd::make_unique(std::move(artboardCopy), + std::move(stateMachineInstance), + listItem); + if (index == -1) + { + m_ListItemsCache.push_back(std::move(cacheListItem)); + } + else + { + m_ListItemsCache.insert(m_ListItemsCache.begin() + index, std::move(cacheListItem)); + } +} + +void DataBindContextValueList::swapItems(Core* target, int index1, int index2) +{ + std::iter_swap(m_ListItemsCache.begin() + index1, m_ListItemsCache.begin() + index2); +} + +void DataBindContextValueList::popItem(Core* target) { m_ListItemsCache.pop_back(); } + +void DataBindContextValueList::update(Core* target) +{ + if (target != nullptr) + { + auto sourceList = m_source->as(); + auto listItems = sourceList->listItems(); + + int listIndex = 0; + while (listIndex < listItems.size()) + { + auto listItem = listItems[listIndex]; + if (listIndex < m_ListItemsCache.size()) + { + if (m_ListItemsCache[listIndex]->listItem() == listItem) + { + // Same item in same position: do nothing + } + else + { + int cacheIndex = listIndex + 1; + bool found = false; + while (cacheIndex < m_ListItemsCache.size()) + { + if (m_ListItemsCache[cacheIndex]->listItem() == listItem) + { + // swap cache position with new item + swapItems(target, listIndex, cacheIndex); + found = true; + break; + } + cacheIndex++; + } + if (!found) + { + // create new element and insert it in listIndex + insertItem(target, listItem, listIndex); + } + } + } + else + { + // create new element and cache the listItem in listIndex + insertItem(target, listItem, -1); + } + + listIndex++; + } + // remove remaining cached elements backwars to pop from the vector. + listIndex = m_ListItemsCache.size() - 1; + while (listIndex >= listItems.size()) + { + popItem(target); + listIndex--; + } + } +} + +void DataBindContextValueList::apply(Core* target, uint32_t propertyKey, bool isMainDirection) {} + +void DataBindContextValueList::applyToSource(Core* target, + uint32_t propertyKey, + bool isMainDirection) +{ + // TODO: @hernan does applyToSource make sense? Should we block it somehow? +} \ No newline at end of file diff --git a/src/data_bind/context/context_value_list_item.cpp b/src/data_bind/context/context_value_list_item.cpp new file mode 100644 index 00000000..07f1a0f4 --- /dev/null +++ b/src/data_bind/context/context_value_list_item.cpp @@ -0,0 +1,11 @@ +#include "rive/data_bind/context/context_value_list_item.hpp" + +using namespace rive; + +DataBindContextValueListItem::DataBindContextValueListItem( + std::unique_ptr artboard, + std::unique_ptr stateMachine, + ViewModelInstanceListItem* listItem) : + m_Artboard(std::move(artboard)), + m_StateMachine(std::move(stateMachine)), + m_ListItem(listItem){}; \ No newline at end of file diff --git a/src/data_bind/context/context_value_number.cpp b/src/data_bind/context/context_value_number.cpp new file mode 100644 index 00000000..4f40997d --- /dev/null +++ b/src/data_bind/context/context_value_number.cpp @@ -0,0 +1,23 @@ +#include "rive/data_bind/context/context_value_number.hpp" +#include "rive/data_bind/data_values/data_value_number.hpp" +#include "rive/generated/core_registry.hpp" + +using namespace rive; + +DataBindContextValueNumber::DataBindContextValueNumber(ViewModelInstanceValue* source, + DataConverter* converter) : + DataBindContextValue(source, converter) +{} + +void DataBindContextValueNumber::apply(Core* target, uint32_t propertyKey, bool isMainDirection) +{ + updateSourceValue(); + auto value = calculateValue(m_dataValue, isMainDirection); + CoreRegistry::setDouble(target, propertyKey, value); +} + +DataValue* DataBindContextValueNumber::getTargetValue(Core* target, uint32_t propertyKey) +{ + auto value = CoreRegistry::getDouble(target, propertyKey); + return new DataValueNumber(value); +} \ No newline at end of file diff --git a/src/data_bind/context/context_value_string.cpp b/src/data_bind/context/context_value_string.cpp new file mode 100644 index 00000000..2c73dc89 --- /dev/null +++ b/src/data_bind/context/context_value_string.cpp @@ -0,0 +1,23 @@ +#include "rive/data_bind/context/context_value_string.hpp" +#include "rive/data_bind/data_values/data_value_string.hpp" +#include "rive/generated/core_registry.hpp" + +using namespace rive; + +DataBindContextValueString::DataBindContextValueString(ViewModelInstanceValue* source, + DataConverter* converter) : + DataBindContextValue(source, converter) +{} + +void DataBindContextValueString::apply(Core* target, uint32_t propertyKey, bool isMainDirection) +{ + updateSourceValue(); + auto value = calculateValue(m_dataValue, isMainDirection); + CoreRegistry::setString(target, propertyKey, value); +} + +DataValue* DataBindContextValueString::getTargetValue(Core* target, uint32_t propertyKey) +{ + auto value = CoreRegistry::getString(target, propertyKey); + return new DataValueString(value); +} \ No newline at end of file diff --git a/src/data_bind/converters/data_converter.cpp b/src/data_bind/converters/data_converter.cpp new file mode 100644 index 00000000..d9645b9a --- /dev/null +++ b/src/data_bind/converters/data_converter.cpp @@ -0,0 +1,18 @@ +#include "rive/data_bind/converters/data_converter.hpp" +#include "rive/importers/import_stack.hpp" +#include "rive/importers/backboard_importer.hpp" +#include "rive/backboard.hpp" + +using namespace rive; + +StatusCode DataConverter::import(ImportStack& importStack) +{ + auto backboardImporter = importStack.latest(Backboard::typeKey); + if (backboardImporter == nullptr) + { + return StatusCode::MissingObject; + } + backboardImporter->addDataConverter(this); + + return Super::import(importStack); +} \ No newline at end of file diff --git a/src/data_bind/converters/data_converter_group.cpp b/src/data_bind/converters/data_converter_group.cpp new file mode 100644 index 00000000..aab150fd --- /dev/null +++ b/src/data_bind/converters/data_converter_group.cpp @@ -0,0 +1,28 @@ +#include "rive/math/math_types.hpp" +#include "rive/data_bind/converters/data_converter_group.hpp" +#include "rive/data_bind/data_values/data_value_number.hpp" +#include "rive/data_bind/data_values/data_value_string.hpp" + +using namespace rive; + +void DataConverterGroup::addItem(DataConverterGroupItem* item) { m_items.push_back(item); } + +DataValue* DataConverterGroup::convert(DataValue* input) +{ + DataValue* value = input; + for (auto item : m_items) + { + value = item->converter()->convert(value); + } + return value; +} + +DataValue* DataConverterGroup::reverseConvert(DataValue* input) +{ + DataValue* value = input; + for (auto it = m_items.rbegin(); it != m_items.rend(); ++it) + { + value = (*it)->converter()->reverseConvert(value); + } + return value; +} \ No newline at end of file diff --git a/src/data_bind/converters/data_converter_group_item.cpp b/src/data_bind/converters/data_converter_group_item.cpp new file mode 100644 index 00000000..3a6fa65e --- /dev/null +++ b/src/data_bind/converters/data_converter_group_item.cpp @@ -0,0 +1,26 @@ +#include "rive/backboard.hpp" +#include "rive/math/math_types.hpp" +#include "rive/data_bind/converters/data_converter_group_item.hpp" +#include "rive/data_bind/converters/data_converter_group.hpp" +#include "rive/importers/data_converter_group_importer.hpp" +#include "rive/importers/backboard_importer.hpp" + +using namespace rive; + +StatusCode DataConverterGroupItem::import(ImportStack& importStack) +{ + auto backboardImporter = importStack.latest(Backboard::typeKey); + if (backboardImporter == nullptr) + { + return StatusCode::MissingObject; + } + backboardImporter->addDataConverterGroupItemReferencer(this); + auto dataConveterGroupImporter = + importStack.latest(DataConverterGroupBase::typeKey); + if (dataConveterGroupImporter == nullptr) + { + return StatusCode::MissingObject; + } + dataConveterGroupImporter->group()->addItem(this); + return Super::import(importStack); +} \ No newline at end of file diff --git a/src/data_bind/converters/data_converter_operation.cpp b/src/data_bind/converters/data_converter_operation.cpp new file mode 100644 index 00000000..dfb43d8a --- /dev/null +++ b/src/data_bind/converters/data_converter_operation.cpp @@ -0,0 +1,65 @@ +#include "rive/math/math_types.hpp" +#include "rive/data_bind/converters/data_converter_operation.hpp" +#include "rive/data_bind/data_values/data_value_number.hpp" + +using namespace rive; + +DataValue* DataConverterOperation::convert(DataValue* input) +{ + auto output = new DataValueNumber(); + if (input->is()) + { + float inputValue = input->as()->value(); + float resultValue = value(); + switch (op()) + { + case ArithmeticOperation::add: + resultValue = inputValue + resultValue; + break; + case ArithmeticOperation::subtract: + resultValue = inputValue - resultValue; + break; + case ArithmeticOperation::multiply: + resultValue = inputValue * resultValue; + break; + case ArithmeticOperation::divide: + resultValue = inputValue / resultValue; + break; + case ArithmeticOperation::modulo: + resultValue = fmodf(inputValue, resultValue); + break; + } + output->value(resultValue); + } + return output; +} + +DataValue* DataConverterOperation::reverseConvert(DataValue* input) +{ + auto output = new DataValueNumber(); + if (input->is()) + { + float inputValue = input->as()->value(); + float resultValue = value(); + switch (op()) + { + case ArithmeticOperation::add: + resultValue = inputValue - resultValue; + break; + case ArithmeticOperation::subtract: + resultValue = inputValue + resultValue; + break; + case ArithmeticOperation::multiply: + resultValue = inputValue / resultValue; + break; + case ArithmeticOperation::divide: + resultValue = inputValue * resultValue; + break; + // No reverse operation for modulo + case ArithmeticOperation::modulo: + break; + } + output->value(resultValue); + } + return output; +} \ No newline at end of file diff --git a/src/data_bind/converters/data_converter_rounder.cpp b/src/data_bind/converters/data_converter_rounder.cpp new file mode 100644 index 00000000..86422ff4 --- /dev/null +++ b/src/data_bind/converters/data_converter_rounder.cpp @@ -0,0 +1,19 @@ +#include "rive/math/math_types.hpp" +#include "rive/data_bind/converters/data_converter_rounder.hpp" +#include "rive/data_bind/data_values/data_value_number.hpp" + +using namespace rive; + +DataValue* DataConverterRounder::convert(DataValue* input) +{ + auto output = new DataValueNumber(); + if (input->is()) + { + float value = input->as()->value(); + auto numberOfPlaces = decimals(); + // TODO: @hernan review this way of rounding + float rounder = pow(10.0f, (float)numberOfPlaces); + output->value(std::round(value * rounder) / rounder); + } + return output; +} \ No newline at end of file diff --git a/src/data_bind/converters/data_converter_to_string.cpp b/src/data_bind/converters/data_converter_to_string.cpp new file mode 100644 index 00000000..6c62f38a --- /dev/null +++ b/src/data_bind/converters/data_converter_to_string.cpp @@ -0,0 +1,36 @@ +#include "rive/math/math_types.hpp" +#include "rive/data_bind/converters/data_converter_to_string.hpp" +#include "rive/data_bind/data_values/data_value_number.hpp" +#include "rive/data_bind/data_values/data_value_enum.hpp" +#include "rive/data_bind/data_values/data_value_string.hpp" + +using namespace rive; + +DataValue* DataConverterToString::convert(DataValue* input) +{ + auto output = new DataValueString(); + if (input->is()) + { + float value = input->as()->value(); + std::string str = std::to_string(value); + if (str.find('.') != std::string::npos) + { + // Remove trailing zeroes + str = str.substr(0, str.find_last_not_of('0') + 1); + // If the decimal point is now the last character, remove that as well + if (str.find('.') == str.size() - 1) + { + str = str.substr(0, str.size() - 1); + } + } + output->value(str); + } + else if (input->is()) + { + auto dataEnum = input->as()->dataEnum(); + auto index = input->as()->value(); + auto enumValue = dataEnum->value(index); + output->value(enumValue); + } + return output; +} \ No newline at end of file diff --git a/src/data_bind/data_bind.cpp b/src/data_bind/data_bind.cpp new file mode 100644 index 00000000..e4c52c97 --- /dev/null +++ b/src/data_bind/data_bind.cpp @@ -0,0 +1,191 @@ +#include "rive/data_bind/data_bind.hpp" +#include "rive/artboard.hpp" +#include "rive/data_bind_flags.hpp" +#include "rive/generated/core_registry.hpp" +#include "rive/data_bind/bindable_property_number.hpp" +#include "rive/data_bind/bindable_property_string.hpp" +#include "rive/data_bind/bindable_property_color.hpp" +#include "rive/data_bind/bindable_property_enum.hpp" +#include "rive/data_bind/bindable_property_boolean.hpp" +#include "rive/data_bind/context/context_value.hpp" +#include "rive/data_bind/context/context_value_boolean.hpp" +#include "rive/data_bind/context/context_value_number.hpp" +#include "rive/data_bind/context/context_value_string.hpp" +#include "rive/data_bind/context/context_value_enum.hpp" +#include "rive/data_bind/context/context_value_list.hpp" +#include "rive/data_bind/context/context_value_color.hpp" +#include "rive/data_bind/data_values/data_type.hpp" +#include "rive/animation/transition_viewmodel_condition.hpp" +#include "rive/animation/state_machine.hpp" +#include "rive/importers/artboard_importer.hpp" +#include "rive/importers/state_machine_importer.hpp" +#include "rive/importers/backboard_importer.hpp" + +using namespace rive; + +StatusCode DataBind::onAddedDirty(CoreContext* context) +{ + StatusCode code = Super::onAddedDirty(context); + if (code != StatusCode::Ok) + { + return code; + } + + return StatusCode::Ok; +} + +StatusCode DataBind::import(ImportStack& importStack) +{ + + auto backboardImporter = importStack.latest(Backboard::typeKey); + if (backboardImporter == nullptr) + { + return StatusCode::MissingObject; + } + backboardImporter->addDataConverterReferencer(this); + if (target()) + { + switch (target()->coreType()) + { + case BindablePropertyNumberBase::typeKey: + case BindablePropertyStringBase::typeKey: + case BindablePropertyBooleanBase::typeKey: + case BindablePropertyEnumBase::typeKey: + case BindablePropertyColorBase::typeKey: + case TransitionPropertyViewModelComparatorBase::typeKey: + { + auto stateMachineImporter = + importStack.latest(StateMachineBase::typeKey); + if (stateMachineImporter != nullptr) + { + stateMachineImporter->addDataBind(std::unique_ptr(this)); + return Super::import(importStack); + } + break; + } + default: + { + auto artboardImporter = importStack.latest(ArtboardBase::typeKey); + if (artboardImporter != nullptr) + { + artboardImporter->addDataBind(this); + return Super::import(importStack); + } + break; + } + } + } + + return Super::import(importStack); +} + +DataType DataBind::outputType() +{ + if (converter()) + { + return converter()->outputType(); + } + switch (m_Source->coreType()) + { + case ViewModelInstanceNumberBase::typeKey: + return DataType::number; + case ViewModelInstanceStringBase::typeKey: + return DataType::string; + case ViewModelInstanceEnumBase::typeKey: + return DataType::enumType; + case ViewModelInstanceColorBase::typeKey: + return DataType::color; + case ViewModelInstanceBooleanBase::typeKey: + return DataType::boolean; + case ViewModelInstanceListBase::typeKey: + return DataType::list; + } + return DataType::none; +} + +void DataBind::bind() +{ + switch (outputType()) + { + case DataType::number: + m_ContextValue = + rivestd::make_unique(m_Source, converter()); + break; + case DataType::string: + m_ContextValue = + rivestd::make_unique(m_Source, converter()); + break; + case DataType::boolean: + m_ContextValue = + rivestd::make_unique(m_Source, converter()); + break; + case DataType::color: + m_ContextValue = rivestd::make_unique(m_Source, converter()); + break; + case DataType::enumType: + m_ContextValue = rivestd::make_unique(m_Source, converter()); + break; + case DataType::list: + m_ContextValue = rivestd::make_unique(m_Source, converter()); + m_ContextValue->update(m_target); + break; + default: + break; + } +} + +void DataBind::update(ComponentDirt value) +{ + if (m_Source != nullptr && m_ContextValue != nullptr) + { + + // Use the ComponentDirt::Components flag to indicate the viewmodel has added or removed + // an element to a list. + if ((value & ComponentDirt::Components) == ComponentDirt::Components) + { + m_ContextValue->update(m_target); + } + if ((value & ComponentDirt::Bindings) == ComponentDirt::Bindings) + { + // TODO: @hernan review how dirt and mode work together. If dirt is not set for + // certain modes, we might be able to skip the mode validation. + auto flagsValue = static_cast(flags()); + if (((flagsValue & DataBindFlags::Direction) == DataBindFlags::ToTarget) || + ((flagsValue & DataBindFlags::TwoWay) == DataBindFlags::TwoWay)) + { + m_ContextValue->apply(m_target, + propertyKey(), + (flagsValue & DataBindFlags::Direction) == + DataBindFlags::ToTarget); + } + } + } +} + +void DataBind::updateSourceBinding() +{ + auto flagsValue = static_cast(flags()); + if (((flagsValue & DataBindFlags::Direction) == DataBindFlags::ToSource) || + ((flagsValue & DataBindFlags::TwoWay) == DataBindFlags::TwoWay)) + { + if (m_ContextValue != nullptr) + { + m_ContextValue->applyToSource(m_target, + propertyKey(), + (flagsValue & DataBindFlags::Direction) == + DataBindFlags::ToSource); + } + } +} + +bool DataBind::addDirt(ComponentDirt value, bool recurse) +{ + if ((m_Dirt & value) == value) + { + // Already marked. + return false; + } + + m_Dirt |= value; + return true; +} \ No newline at end of file diff --git a/src/data_bind/data_bind_context.cpp b/src/data_bind/data_bind_context.cpp new file mode 100644 index 00000000..62eae73f --- /dev/null +++ b/src/data_bind/data_bind_context.cpp @@ -0,0 +1,41 @@ +#include "rive/data_bind_flags.hpp" +#include "rive/data_bind/data_bind_context.hpp" +#include "rive/data_bind/context/context_value_number.hpp" +#include "rive/data_bind/context/context_value_string.hpp" +#include "rive/data_bind/context/context_value_enum.hpp" +#include "rive/data_bind/context/context_value_list.hpp" +#include "rive/data_bind/context/context_value_color.hpp" +#include "rive/artboard.hpp" +#include "rive/generated/core_registry.hpp" +#include + +using namespace rive; + +void DataBindContext::decodeSourcePathIds(Span value) +{ + BinaryReader reader(value); + while (!reader.reachedEnd()) + { + auto val = reader.readVarUintAs(); + m_SourcePathIdsBuffer.push_back(val); + } +} + +void DataBindContext::copySourcePathIds(const DataBindContextBase& object) +{ + m_SourcePathIdsBuffer = object.as()->m_SourcePathIdsBuffer; +} + +void DataBindContext::bindFromContext(DataContext* dataContext) +{ + if (dataContext != nullptr) + { + auto value = dataContext->getViewModelProperty(m_SourcePathIdsBuffer); + if (value != nullptr) + { + value->addDependent(this); + m_Source = value; + bind(); + } + } +} \ No newline at end of file diff --git a/src/data_bind/data_context.cpp b/src/data_bind/data_context.cpp new file mode 100644 index 00000000..a8b3f307 --- /dev/null +++ b/src/data_bind/data_context.cpp @@ -0,0 +1,90 @@ +#include "rive/data_bind/data_context.hpp" +#include "rive/viewmodel/viewmodel_instance_viewmodel.hpp" + +using namespace rive; + +DataContext::DataContext(ViewModelInstance* viewModelInstance) : + m_ViewModelInstance(viewModelInstance) +{} + +DataContext::DataContext() : m_ViewModelInstances({}) {} + +DataContext::~DataContext() {} + +void DataContext::addViewModelInstance(ViewModelInstance* value) +{ + m_ViewModelInstances.push_back(value); +} + +void DataContext::viewModelInstance(ViewModelInstance* value) { m_ViewModelInstance = value; } + +ViewModelInstanceValue* DataContext::getViewModelProperty(const std::vector path) const +{ + std::vector::const_iterator it; + if (path.size() == 0) + { + return nullptr; + } + // TODO: @hernan review. We should probably remove the std::vector and only keep the instance + for (auto viewModel : m_ViewModelInstances) + { + if (viewModel->viewModelId() == path[0]) + { + ViewModelInstance* instance = viewModel; + for (it = path.begin() + 1; it != path.end() - 1; it++) + { + instance = instance->propertyValue(*it) + ->as() + ->referenceViewModelInstance(); + } + ViewModelInstanceValue* value = instance->propertyValue(*it++); + return value; + } + } + if (m_ViewModelInstance != nullptr && m_ViewModelInstance->viewModelId() == path[0]) + { + ViewModelInstance* instance = m_ViewModelInstance; + for (it = path.begin() + 1; it != path.end() - 1; it++) + { + instance = instance->propertyValue(*it) + ->as() + ->referenceViewModelInstance(); + } + ViewModelInstanceValue* value = instance->propertyValue(*it++); + return value; + } + if (m_Parent != nullptr) + { + return m_Parent->getViewModelProperty(path); + } + return nullptr; +} + +ViewModelInstance* DataContext::getViewModelInstance(const std::vector path) const +{ + std::vector::const_iterator it; + if (path.size() == 0) + { + return nullptr; + } + if (m_ViewModelInstance != nullptr && m_ViewModelInstance->viewModelId() == path[0]) + { + ViewModelInstance* instance = m_ViewModelInstance; + for (it = path.begin() + 1; it != path.end(); it++) + { + instance = instance->propertyValue(*it) + ->as() + ->referenceViewModelInstance(); + if (instance == nullptr) + { + return instance; + } + } + return instance; + } + if (m_Parent != nullptr) + { + return m_Parent->getViewModelInstance(path); + } + return nullptr; +} \ No newline at end of file diff --git a/src/dependency_sorter.cpp b/src/dependency_sorter.cpp index 1bf9f44d..dcfa266e 100644 --- a/src/dependency_sorter.cpp +++ b/src/dependency_sorter.cpp @@ -9,6 +9,15 @@ void DependencySorter::sort(Component* root, std::vector& order) visit(root, order); } +void DependencySorter::sort(std::vector roots, std::vector& order) +{ + order.clear(); + for (auto root : roots) + { + visit(root, order); + } +} + bool DependencySorter::visit(Component* component, std::vector& order) { if (m_Perm.find(component) != m_Perm.end()) diff --git a/src/drawable.cpp b/src/drawable.cpp index 5f41d68f..0dc9ca07 100644 --- a/src/drawable.cpp +++ b/src/drawable.cpp @@ -1,5 +1,6 @@ #include "rive/drawable.hpp" #include "rive/artboard.hpp" +#include "rive/layout_component.hpp" #include "rive/shapes/clipping_shape.hpp" #include "rive/shapes/path_composer.hpp" #include "rive/shapes/shape.hpp" @@ -7,9 +8,40 @@ using namespace rive; +StatusCode Drawable::onAddedDirty(CoreContext* context) +{ + auto code = Super::onAddedDirty(context); + if (code != StatusCode::Ok) + { + return code; + } + auto blendMode = static_cast(blendModeValue()); + switch (blendMode) + { + case rive::BlendMode::srcOver: + case rive::BlendMode::screen: + case rive::BlendMode::overlay: + case rive::BlendMode::darken: + case rive::BlendMode::lighten: + case rive::BlendMode::colorDodge: + case rive::BlendMode::colorBurn: + case rive::BlendMode::hardLight: + case rive::BlendMode::softLight: + case rive::BlendMode::difference: + case rive::BlendMode::exclusion: + case rive::BlendMode::multiply: + case rive::BlendMode::hue: + case rive::BlendMode::saturation: + case rive::BlendMode::color: + case rive::BlendMode::luminosity: + return StatusCode::Ok; + } + return StatusCode::InvalidObject; +} + void Drawable::addClippingShape(ClippingShape* shape) { m_ClippingShapes.push_back(shape); } -ClipResult Drawable::clip(Renderer* renderer) const +ClipResult Drawable::applyClip(Renderer* renderer) const { if (m_ClippingShapes.size() == 0) { @@ -39,4 +71,16 @@ ClipResult Drawable::clip(Renderer* renderer) const } } return ClipResult::clip; +} + +bool Drawable::isChildOfLayout(LayoutComponent* layout) +{ + for (ContainerComponent* parent = this; parent != nullptr; parent = parent->parent()) + { + if (parent->is() && parent->as() == layout) + { + return true; + } + } + return false; } \ No newline at end of file diff --git a/src/factory.cpp b/src/factory.cpp index 00b89c1a..09c670a9 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -5,6 +5,7 @@ #include "rive/factory.hpp" #include "rive/math/aabb.hpp" #include "rive/math/raw_path.hpp" +#include "rive/text/raw_text.hpp" #ifdef WITH_RIVE_TEXT #include "rive/text/font_hb.hpp" #endif @@ -26,3 +27,12 @@ rcp Factory::decodeFont(Span span) return nullptr; #endif } + +rcp Factory::decodeAudio(Span span) +{ +#ifdef WITH_RIVE_AUDIO + return rcp(new AudioSource(SimpleArray(span.data(), span.size()))); +#else + return nullptr; +#endif +} diff --git a/src/file.cpp b/src/file.cpp index 183aa504..db3e98f0 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -8,6 +8,9 @@ #include "rive/generated/core_registry.hpp" #include "rive/importers/artboard_importer.hpp" #include "rive/importers/backboard_importer.hpp" +#include "rive/importers/bindable_property_importer.hpp" +#include "rive/importers/data_converter_group_importer.hpp" +#include "rive/importers/enum_importer.hpp" #include "rive/importers/file_asset_importer.hpp" #include "rive/importers/import_stack.hpp" #include "rive/importers/keyed_object_importer.hpp" @@ -19,6 +22,10 @@ #include "rive/importers/layer_state_importer.hpp" #include "rive/importers/state_transition_importer.hpp" #include "rive/importers/state_machine_layer_component_importer.hpp" +#include "rive/importers/transition_viewmodel_condition_importer.hpp" +#include "rive/importers/viewmodel_importer.hpp" +#include "rive/importers/viewmodel_instance_importer.hpp" +#include "rive/importers/viewmodel_instance_list_importer.hpp" #include "rive/animation/blend_state_transition.hpp" #include "rive/animation/any_state.hpp" #include "rive/animation/entry_state.hpp" @@ -26,9 +33,29 @@ #include "rive/animation/animation_state.hpp" #include "rive/animation/blend_state_1d.hpp" #include "rive/animation/blend_state_direct.hpp" +#include "rive/animation/transition_property_viewmodel_comparator.hpp" +#include "rive/data_bind/bindable_property.hpp" +#include "rive/data_bind/bindable_property_number.hpp" +#include "rive/data_bind/bindable_property_string.hpp" +#include "rive/data_bind/bindable_property_color.hpp" +#include "rive/data_bind/bindable_property_enum.hpp" +#include "rive/data_bind/bindable_property_boolean.hpp" +#include "rive/data_bind/converters/data_converter_group.hpp" #include "rive/assets/file_asset.hpp" #include "rive/assets/audio_asset.hpp" #include "rive/assets/file_asset_contents.hpp" +#include "rive/viewmodel/viewmodel.hpp" +#include "rive/viewmodel/data_enum.hpp" +#include "rive/viewmodel/viewmodel_instance.hpp" +#include "rive/viewmodel/viewmodel_instance_list.hpp" +#include "rive/viewmodel/viewmodel_instance_viewmodel.hpp" +#include "rive/viewmodel/viewmodel_instance_number.hpp" +#include "rive/viewmodel/viewmodel_instance_string.hpp" +#include "rive/viewmodel/viewmodel_property_viewmodel.hpp" +#include "rive/viewmodel/viewmodel_property_string.hpp" +#include "rive/viewmodel/viewmodel_property_number.hpp" +#include "rive/viewmodel/viewmodel_property_enum.hpp" +#include "rive/viewmodel/viewmodel_property_list.hpp" // Default namespace for Rive Cpp code using namespace rive; @@ -171,16 +198,16 @@ std::unique_ptr File::import(Span bytes, } return nullptr; } - auto file = std::unique_ptr(new File(factory, assetLoader)); + auto file = rivestd::make_unique(factory, assetLoader); auto readResult = file->read(reader, header); - if (readResult != ImportResult::success) + if (result) { - file.reset(nullptr); + *result = readResult; } - if (result) + if (readResult != ImportResult::success) { - *result = ImportResult::success; + file.reset(nullptr); } return file; } @@ -188,6 +215,10 @@ std::unique_ptr File::import(Span bytes, ImportResult File::read(BinaryReader& reader, const RuntimeHeader& header) { ImportStack importStack; + // TODO: @hernan consider moving this to a special importer. It's not that + // simple because Core doesn't have a typeKey, so it should be treated as + // a special case. In any case, it's not that bad having it here for now. + Core* lastBindableObject; while (!reader.reachedEnd()) { auto object = readRuntimeObject(reader, header); @@ -196,6 +227,14 @@ ImportResult File::read(BinaryReader& reader, const RuntimeHeader& header) importStack.readNullObject(); continue; } + if (!object->is()) + { + lastBindableObject = object; + } + else if (lastBindableObject != nullptr) + { + object->as()->target(lastBindableObject); + } if (object->import(importStack) == StatusCode::Ok) { switch (object->coreType()) @@ -218,6 +257,24 @@ ImportResult File::read(BinaryReader& reader, const RuntimeHeader& header) m_fileAssets.push_back(fa); } break; + case ViewModel::typeKey: + { + auto vmc = object->as(); + m_ViewModels.push_back(vmc); + break; + } + case DataEnum::typeKey: + { + auto de = object->as(); + m_Enums.push_back(de); + break; + } + case ViewModelPropertyEnum::typeKey: + { + auto vme = object->as(); + vme->dataEnum(m_Enums[vme->enumId()]); + } + break; } } else @@ -226,22 +283,26 @@ ImportResult File::read(BinaryReader& reader, const RuntimeHeader& header) delete object; continue; } - ImportStackObject* stackObject = nullptr; + std::unique_ptr stackObject = nullptr; auto stackType = object->coreType(); switch (stackType) { case Backboard::typeKey: - stackObject = new BackboardImporter(object->as()); + stackObject = rivestd::make_unique(object->as()); break; case Artboard::typeKey: - stackObject = new ArtboardImporter(object->as()); + stackObject = rivestd::make_unique(object->as()); + break; + case DataEnum::typeKey: + stackObject = rivestd::make_unique(object->as()); break; case LinearAnimation::typeKey: - stackObject = new LinearAnimationImporter(object->as()); + stackObject = + rivestd::make_unique(object->as()); break; case KeyedObject::typeKey: - stackObject = new KeyedObjectImporter(object->as()); + stackObject = rivestd::make_unique(object->as()); break; case KeyedProperty::typeKey: { @@ -252,11 +313,13 @@ ImportResult File::read(BinaryReader& reader, const RuntimeHeader& header) return ImportResult::malformed; } stackObject = - new KeyedPropertyImporter(importer->animation(), object->as()); + rivestd::make_unique(importer->animation(), + object->as()); break; } case StateMachine::typeKey: - stackObject = new StateMachineImporter(object->as()); + stackObject = + rivestd::make_unique(object->as()); break; case StateMachineLayer::typeKey: { @@ -266,8 +329,9 @@ ImportResult File::read(BinaryReader& reader, const RuntimeHeader& header) return ImportResult::malformed; } - stackObject = new StateMachineLayerImporter(object->as(), - artboardImporter->artboard()); + stackObject = + rivestd::make_unique(object->as(), + artboardImporter->artboard()); break; } @@ -277,33 +341,70 @@ ImportResult File::read(BinaryReader& reader, const RuntimeHeader& header) case AnimationState::typeKey: case BlendState1D::typeKey: case BlendStateDirect::typeKey: - stackObject = new LayerStateImporter(object->as()); + stackObject = rivestd::make_unique(object->as()); stackType = LayerState::typeKey; break; case StateTransition::typeKey: case BlendStateTransition::typeKey: - stackObject = new StateTransitionImporter(object->as()); + stackObject = + rivestd::make_unique(object->as()); stackType = StateTransition::typeKey; break; case StateMachineListener::typeKey: - stackObject = new StateMachineListenerImporter(object->as()); + stackObject = rivestd::make_unique( + object->as()); break; case ImageAsset::typeKey: case FontAsset::typeKey: case AudioAsset::typeKey: - stackObject = - new FileAssetImporter(object->as(), m_assetLoader, m_factory); + stackObject = rivestd::make_unique(object->as(), + m_assetLoader, + m_factory); stackType = FileAsset::typeKey; break; + case ViewModel::typeKey: + stackObject = rivestd::make_unique(object->as()); + stackType = ViewModel::typeKey; + break; + case ViewModelInstance::typeKey: + stackObject = rivestd::make_unique( + object->as()); + stackType = ViewModelInstance::typeKey; + break; + case ViewModelInstanceList::typeKey: + stackObject = rivestd::make_unique( + object->as()); + stackType = ViewModelInstanceList::typeKey; + break; + case TransitionViewModelCondition::typeKey: + case TransitionArtboardCondition::typeKey: + stackObject = rivestd::make_unique( + object->as()); + stackType = TransitionViewModelCondition::typeKey; + break; + case BindablePropertyNumber::typeKey: + case BindablePropertyString::typeKey: + case BindablePropertyColor::typeKey: + case BindablePropertyEnum::typeKey: + case BindablePropertyBoolean::typeKey: + stackObject = + rivestd::make_unique(object->as()); + stackType = BindablePropertyBase::typeKey; + break; + case DataConverterGroupBase::typeKey: + stackObject = rivestd::make_unique( + object->as()); + stackType = DataConverterGroupBase::typeKey; + break; } - if (importStack.makeLatest(stackType, stackObject) != StatusCode::Ok) + if (importStack.makeLatest(stackType, std::move(stackObject)) != StatusCode::Ok) { // Some previous stack item didn't resolve. return ImportResult::malformed; } if (object->is() && importStack.makeLatest(StateMachineLayerComponent::typeKey, - new StateMachineLayerComponentImporter( + rivestd::make_unique( object->as())) != StatusCode::Ok) { return ImportResult::malformed; @@ -368,6 +469,148 @@ std::unique_ptr File::artboardNamed(std::string name) const return ab ? ab->instance() : nullptr; } +void File::completeViewModelInstance(ViewModelInstance* viewModelInstance) +{ + auto viewModel = m_ViewModels[viewModelInstance->viewModelId()]; + auto propertyValues = viewModelInstance->propertyValues(); + for (auto value : propertyValues) + { + if (value->is()) + { + auto property = viewModel->property(value->viewModelPropertyId()); + if (property->is()) + { + auto valueViewModel = value->as(); + auto propertViewModel = property->as(); + auto viewModelReference = m_ViewModels[propertViewModel->viewModelReferenceId()]; + auto viewModelInstance = + viewModelReference->instance(valueViewModel->propertyValue()); + if (viewModelInstance != nullptr) + { + valueViewModel->referenceViewModelInstance( + copyViewModelInstance(viewModelInstance)); + } + } + } + else if (value->is()) + { + auto viewModelList = value->as(); + for (auto listItem : viewModelList->listItems()) + { + auto viewModel = m_ViewModels[listItem->viewModelId()]; + auto viewModelInstance = viewModel->instance(listItem->viewModelInstanceId()); + listItem->viewModelInstance(copyViewModelInstance(viewModelInstance)); + if (listItem->artboardId() < m_artboards.size()) + { + listItem->artboard(m_artboards[listItem->artboardId()]); + } + } + } + value->viewModelProperty(viewModel->property(value->viewModelPropertyId())); + } +} + +ViewModelInstance* File::copyViewModelInstance(ViewModelInstance* viewModelInstance) +{ + auto copy = viewModelInstance->clone()->as(); + completeViewModelInstance(copy); + return copy; +} + +ViewModelInstance* File::createViewModelInstance(std::string name) +{ + for (auto viewModel : m_ViewModels) + { + if (viewModel->is()) + { + if (viewModel->name() == name) + { + return createViewModelInstance(viewModel); + } + } + } + return nullptr; +} + +ViewModelInstance* File::createViewModelInstance(std::string name, std::string instanceName) +{ + for (auto viewModel : m_ViewModels) + { + if (viewModel->is()) + { + if (viewModel->name() == name) + { + auto instance = viewModel->instance(instanceName); + if (instance != nullptr) + { + return copyViewModelInstance(instance); + } + } + } + } + return nullptr; +} + +ViewModelInstance* File::createViewModelInstance(ViewModel* viewModel) +{ + if (viewModel != nullptr) + { + auto viewModelInstance = viewModel->defaultInstance(); + return copyViewModelInstance(viewModelInstance); + } + return nullptr; +} + +ViewModelInstance* File::createViewModelInstance(Artboard* artboard) +{ + if ((size_t)artboard->viewModelId() < m_ViewModels.size()) + { + auto viewModel = m_ViewModels[artboard->viewModelId()]; + if (viewModel != nullptr) + { + return createViewModelInstance(viewModel); + } + } + return nullptr; +} + +ViewModelInstanceListItem* File::viewModelInstanceListItem(ViewModelInstance* viewModelInstance) +{ + // Search for an implicit artboard linked to the viewModel. + // It will return the first one it finds, but there could be more. + // We should decide if we want to be more restrictive and only return + // an artboard if one and only one is found. + for (auto artboard : m_artboards) + { + if (artboard->viewModelId() == viewModelInstance->viewModelId()) + { + return viewModelInstanceListItem(viewModelInstance, artboard); + } + } + return nullptr; +} + +ViewModelInstanceListItem* File::viewModelInstanceListItem(ViewModelInstance* viewModelInstance, + Artboard* artboard) +{ + auto viewModelInstanceListItem = new ViewModelInstanceListItem(); + viewModelInstanceListItem->viewModelInstance(viewModelInstance); + viewModelInstanceListItem->artboard(artboard); + return viewModelInstanceListItem; +} + +ViewModel* File::viewModel(std::string name) +{ + for (auto viewModel : m_ViewModels) + { + if (viewModel->name() == name) + { + return viewModel; + } + } + return nullptr; +} + const std::vector& File::assets() const { return m_fileAssets; } #ifdef WITH_RIVE_TOOLS diff --git a/src/generated/animation/keyframe_uint_base.cpp b/src/generated/animation/keyframe_uint_base.cpp new file mode 100644 index 00000000..35ddbed4 --- /dev/null +++ b/src/generated/animation/keyframe_uint_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/animation/keyframe_uint_base.hpp" +#include "rive/animation/keyframe_uint.hpp" + +using namespace rive; + +Core* KeyFrameUintBase::clone() const +{ + auto cloned = new KeyFrameUint(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/animation/listener_viewmodel_change_base.cpp b/src/generated/animation/listener_viewmodel_change_base.cpp new file mode 100644 index 00000000..96d9e9a2 --- /dev/null +++ b/src/generated/animation/listener_viewmodel_change_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/animation/listener_viewmodel_change_base.hpp" +#include "rive/animation/listener_viewmodel_change.hpp" + +using namespace rive; + +Core* ListenerViewModelChangeBase::clone() const +{ + auto cloned = new ListenerViewModelChange(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/animation/transition_artboard_condition_base.cpp b/src/generated/animation/transition_artboard_condition_base.cpp new file mode 100644 index 00000000..4adb3c8b --- /dev/null +++ b/src/generated/animation/transition_artboard_condition_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/animation/transition_artboard_condition_base.hpp" +#include "rive/animation/transition_artboard_condition.hpp" + +using namespace rive; + +Core* TransitionArtboardConditionBase::clone() const +{ + auto cloned = new TransitionArtboardCondition(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/animation/transition_property_artboard_comparator_base.cpp b/src/generated/animation/transition_property_artboard_comparator_base.cpp new file mode 100644 index 00000000..31a068a2 --- /dev/null +++ b/src/generated/animation/transition_property_artboard_comparator_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/animation/transition_property_artboard_comparator_base.hpp" +#include "rive/animation/transition_property_artboard_comparator.hpp" + +using namespace rive; + +Core* TransitionPropertyArtboardComparatorBase::clone() const +{ + auto cloned = new TransitionPropertyArtboardComparator(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/animation/transition_property_viewmodel_comparator_base.cpp b/src/generated/animation/transition_property_viewmodel_comparator_base.cpp new file mode 100644 index 00000000..8c0c2d64 --- /dev/null +++ b/src/generated/animation/transition_property_viewmodel_comparator_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/animation/transition_property_viewmodel_comparator_base.hpp" +#include "rive/animation/transition_property_viewmodel_comparator.hpp" + +using namespace rive; + +Core* TransitionPropertyViewModelComparatorBase::clone() const +{ + auto cloned = new TransitionPropertyViewModelComparator(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/animation/transition_value_boolean_comparator_base.cpp b/src/generated/animation/transition_value_boolean_comparator_base.cpp new file mode 100644 index 00000000..3e9a2ef7 --- /dev/null +++ b/src/generated/animation/transition_value_boolean_comparator_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/animation/transition_value_boolean_comparator_base.hpp" +#include "rive/animation/transition_value_boolean_comparator.hpp" + +using namespace rive; + +Core* TransitionValueBooleanComparatorBase::clone() const +{ + auto cloned = new TransitionValueBooleanComparator(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/animation/transition_value_color_comparator_base.cpp b/src/generated/animation/transition_value_color_comparator_base.cpp new file mode 100644 index 00000000..a199f5fe --- /dev/null +++ b/src/generated/animation/transition_value_color_comparator_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/animation/transition_value_color_comparator_base.hpp" +#include "rive/animation/transition_value_color_comparator.hpp" + +using namespace rive; + +Core* TransitionValueColorComparatorBase::clone() const +{ + auto cloned = new TransitionValueColorComparator(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/animation/transition_value_enum_comparator_base.cpp b/src/generated/animation/transition_value_enum_comparator_base.cpp new file mode 100644 index 00000000..dad87896 --- /dev/null +++ b/src/generated/animation/transition_value_enum_comparator_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/animation/transition_value_enum_comparator_base.hpp" +#include "rive/animation/transition_value_enum_comparator.hpp" + +using namespace rive; + +Core* TransitionValueEnumComparatorBase::clone() const +{ + auto cloned = new TransitionValueEnumComparator(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/animation/transition_value_number_comparator_base.cpp b/src/generated/animation/transition_value_number_comparator_base.cpp new file mode 100644 index 00000000..8ebecac8 --- /dev/null +++ b/src/generated/animation/transition_value_number_comparator_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/animation/transition_value_number_comparator_base.hpp" +#include "rive/animation/transition_value_number_comparator.hpp" + +using namespace rive; + +Core* TransitionValueNumberComparatorBase::clone() const +{ + auto cloned = new TransitionValueNumberComparator(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/animation/transition_value_string_comparator_base.cpp b/src/generated/animation/transition_value_string_comparator_base.cpp new file mode 100644 index 00000000..07db01d5 --- /dev/null +++ b/src/generated/animation/transition_value_string_comparator_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/animation/transition_value_string_comparator_base.hpp" +#include "rive/animation/transition_value_string_comparator.hpp" + +using namespace rive; + +Core* TransitionValueStringComparatorBase::clone() const +{ + auto cloned = new TransitionValueStringComparator(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/animation/transition_viewmodel_condition_base.cpp b/src/generated/animation/transition_viewmodel_condition_base.cpp new file mode 100644 index 00000000..a0d3b254 --- /dev/null +++ b/src/generated/animation/transition_viewmodel_condition_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/animation/transition_viewmodel_condition_base.hpp" +#include "rive/animation/transition_viewmodel_condition.hpp" + +using namespace rive; + +Core* TransitionViewModelConditionBase::clone() const +{ + auto cloned = new TransitionViewModelCondition(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/data_bind/bindable_property_boolean_base.cpp b/src/generated/data_bind/bindable_property_boolean_base.cpp new file mode 100644 index 00000000..4e3dcc20 --- /dev/null +++ b/src/generated/data_bind/bindable_property_boolean_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/data_bind/bindable_property_boolean_base.hpp" +#include "rive/data_bind/bindable_property_boolean.hpp" + +using namespace rive; + +Core* BindablePropertyBooleanBase::clone() const +{ + auto cloned = new BindablePropertyBoolean(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/data_bind/bindable_property_color_base.cpp b/src/generated/data_bind/bindable_property_color_base.cpp new file mode 100644 index 00000000..fa64a071 --- /dev/null +++ b/src/generated/data_bind/bindable_property_color_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/data_bind/bindable_property_color_base.hpp" +#include "rive/data_bind/bindable_property_color.hpp" + +using namespace rive; + +Core* BindablePropertyColorBase::clone() const +{ + auto cloned = new BindablePropertyColor(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/data_bind/bindable_property_enum_base.cpp b/src/generated/data_bind/bindable_property_enum_base.cpp new file mode 100644 index 00000000..acd5be69 --- /dev/null +++ b/src/generated/data_bind/bindable_property_enum_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/data_bind/bindable_property_enum_base.hpp" +#include "rive/data_bind/bindable_property_enum.hpp" + +using namespace rive; + +Core* BindablePropertyEnumBase::clone() const +{ + auto cloned = new BindablePropertyEnum(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/data_bind/bindable_property_number_base.cpp b/src/generated/data_bind/bindable_property_number_base.cpp new file mode 100644 index 00000000..95b0453f --- /dev/null +++ b/src/generated/data_bind/bindable_property_number_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/data_bind/bindable_property_number_base.hpp" +#include "rive/data_bind/bindable_property_number.hpp" + +using namespace rive; + +Core* BindablePropertyNumberBase::clone() const +{ + auto cloned = new BindablePropertyNumber(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/data_bind/bindable_property_string_base.cpp b/src/generated/data_bind/bindable_property_string_base.cpp new file mode 100644 index 00000000..04133c7b --- /dev/null +++ b/src/generated/data_bind/bindable_property_string_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/data_bind/bindable_property_string_base.hpp" +#include "rive/data_bind/bindable_property_string.hpp" + +using namespace rive; + +Core* BindablePropertyStringBase::clone() const +{ + auto cloned = new BindablePropertyString(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/data_bind/converters/data_converter_group_base.cpp b/src/generated/data_bind/converters/data_converter_group_base.cpp new file mode 100644 index 00000000..23ee7706 --- /dev/null +++ b/src/generated/data_bind/converters/data_converter_group_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/data_bind/converters/data_converter_group_base.hpp" +#include "rive/data_bind/converters/data_converter_group.hpp" + +using namespace rive; + +Core* DataConverterGroupBase::clone() const +{ + auto cloned = new DataConverterGroup(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/data_bind/converters/data_converter_group_item_base.cpp b/src/generated/data_bind/converters/data_converter_group_item_base.cpp new file mode 100644 index 00000000..8f1e17b4 --- /dev/null +++ b/src/generated/data_bind/converters/data_converter_group_item_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/data_bind/converters/data_converter_group_item_base.hpp" +#include "rive/data_bind/converters/data_converter_group_item.hpp" + +using namespace rive; + +Core* DataConverterGroupItemBase::clone() const +{ + auto cloned = new DataConverterGroupItem(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/data_bind/converters/data_converter_operation_base.cpp b/src/generated/data_bind/converters/data_converter_operation_base.cpp new file mode 100644 index 00000000..77ee84b0 --- /dev/null +++ b/src/generated/data_bind/converters/data_converter_operation_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/data_bind/converters/data_converter_operation_base.hpp" +#include "rive/data_bind/converters/data_converter_operation.hpp" + +using namespace rive; + +Core* DataConverterOperationBase::clone() const +{ + auto cloned = new DataConverterOperation(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/data_bind/converters/data_converter_rounder_base.cpp b/src/generated/data_bind/converters/data_converter_rounder_base.cpp new file mode 100644 index 00000000..e6a03a31 --- /dev/null +++ b/src/generated/data_bind/converters/data_converter_rounder_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/data_bind/converters/data_converter_rounder_base.hpp" +#include "rive/data_bind/converters/data_converter_rounder.hpp" + +using namespace rive; + +Core* DataConverterRounderBase::clone() const +{ + auto cloned = new DataConverterRounder(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/data_bind/converters/data_converter_to_string_base.cpp b/src/generated/data_bind/converters/data_converter_to_string_base.cpp new file mode 100644 index 00000000..c600a8d8 --- /dev/null +++ b/src/generated/data_bind/converters/data_converter_to_string_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/data_bind/converters/data_converter_to_string_base.hpp" +#include "rive/data_bind/converters/data_converter_to_string.hpp" + +using namespace rive; + +Core* DataConverterToStringBase::clone() const +{ + auto cloned = new DataConverterToString(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/data_bind/data_bind_base.cpp b/src/generated/data_bind/data_bind_base.cpp new file mode 100644 index 00000000..cc993f88 --- /dev/null +++ b/src/generated/data_bind/data_bind_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/data_bind/data_bind_base.hpp" +#include "rive/data_bind/data_bind.hpp" + +using namespace rive; + +Core* DataBindBase::clone() const +{ + auto cloned = new DataBind(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/data_bind/data_bind_context_base.cpp b/src/generated/data_bind/data_bind_context_base.cpp new file mode 100644 index 00000000..4691f4ca --- /dev/null +++ b/src/generated/data_bind/data_bind_context_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/data_bind/data_bind_context_base.hpp" +#include "rive/data_bind/data_bind_context.hpp" + +using namespace rive; + +Core* DataBindContextBase::clone() const +{ + auto cloned = new DataBindContext(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/layout/axis_x_base.cpp b/src/generated/layout/axis_x_base.cpp new file mode 100644 index 00000000..172c2844 --- /dev/null +++ b/src/generated/layout/axis_x_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/layout/axis_x_base.hpp" +#include "rive/layout/axis_x.hpp" + +using namespace rive; + +Core* AxisXBase::clone() const +{ + auto cloned = new AxisX(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/layout/axis_y_base.cpp b/src/generated/layout/axis_y_base.cpp new file mode 100644 index 00000000..8815cefc --- /dev/null +++ b/src/generated/layout/axis_y_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/layout/axis_y_base.hpp" +#include "rive/layout/axis_y.hpp" + +using namespace rive; + +Core* AxisYBase::clone() const +{ + auto cloned = new AxisY(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/layout/layout_component_style_base.cpp b/src/generated/layout/layout_component_style_base.cpp new file mode 100644 index 00000000..22c4da87 --- /dev/null +++ b/src/generated/layout/layout_component_style_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/layout/layout_component_style_base.hpp" +#include "rive/layout/layout_component_style.hpp" + +using namespace rive; + +Core* LayoutComponentStyleBase::clone() const +{ + auto cloned = new LayoutComponentStyle(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/layout/n_slicer_base.cpp b/src/generated/layout/n_slicer_base.cpp new file mode 100644 index 00000000..49d6f377 --- /dev/null +++ b/src/generated/layout/n_slicer_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/layout/n_slicer_base.hpp" +#include "rive/layout/n_slicer.hpp" + +using namespace rive; + +Core* NSlicerBase::clone() const +{ + auto cloned = new NSlicer(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/layout/n_slicer_tile_mode_base.cpp b/src/generated/layout/n_slicer_tile_mode_base.cpp new file mode 100644 index 00000000..068c59ce --- /dev/null +++ b/src/generated/layout/n_slicer_tile_mode_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/layout/n_slicer_tile_mode_base.hpp" +#include "rive/layout/n_slicer_tile_mode.hpp" + +using namespace rive; + +Core* NSlicerTileModeBase::clone() const +{ + auto cloned = new NSlicerTileMode(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/layout_component_base.cpp b/src/generated/layout_component_base.cpp new file mode 100644 index 00000000..13e6c86d --- /dev/null +++ b/src/generated/layout_component_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/layout_component_base.hpp" +#include "rive/layout_component.hpp" + +using namespace rive; + +Core* LayoutComponentBase::clone() const +{ + auto cloned = new LayoutComponent(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/nested_artboard_layout_base.cpp b/src/generated/nested_artboard_layout_base.cpp new file mode 100644 index 00000000..069c3746 --- /dev/null +++ b/src/generated/nested_artboard_layout_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/nested_artboard_layout_base.hpp" +#include "rive/nested_artboard_layout.hpp" + +using namespace rive; + +Core* NestedArtboardLayoutBase::clone() const +{ + auto cloned = new NestedArtboardLayout(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/nested_artboard_leaf_base.cpp b/src/generated/nested_artboard_leaf_base.cpp new file mode 100644 index 00000000..d025a17f --- /dev/null +++ b/src/generated/nested_artboard_leaf_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/nested_artboard_leaf_base.hpp" +#include "rive/nested_artboard_leaf.hpp" + +using namespace rive; + +Core* NestedArtboardLeafBase::clone() const +{ + auto cloned = new NestedArtboardLeaf(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/data_enum_base.cpp b/src/generated/viewmodel/data_enum_base.cpp new file mode 100644 index 00000000..9ac0ef0a --- /dev/null +++ b/src/generated/viewmodel/data_enum_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/data_enum_base.hpp" +#include "rive/viewmodel/data_enum.hpp" + +using namespace rive; + +Core* DataEnumBase::clone() const +{ + auto cloned = new DataEnum(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/data_enum_value_base.cpp b/src/generated/viewmodel/data_enum_value_base.cpp new file mode 100644 index 00000000..f695edc5 --- /dev/null +++ b/src/generated/viewmodel/data_enum_value_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/data_enum_value_base.hpp" +#include "rive/viewmodel/data_enum_value.hpp" + +using namespace rive; + +Core* DataEnumValueBase::clone() const +{ + auto cloned = new DataEnumValue(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_base.cpp b/src/generated/viewmodel/viewmodel_base.cpp new file mode 100644 index 00000000..87e7f7a1 --- /dev/null +++ b/src/generated/viewmodel/viewmodel_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_base.hpp" +#include "rive/viewmodel/viewmodel.hpp" + +using namespace rive; + +Core* ViewModelBase::clone() const +{ + auto cloned = new ViewModel(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_component_base.cpp b/src/generated/viewmodel/viewmodel_component_base.cpp new file mode 100644 index 00000000..706138b9 --- /dev/null +++ b/src/generated/viewmodel/viewmodel_component_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_component_base.hpp" +#include "rive/viewmodel/viewmodel_component.hpp" + +using namespace rive; + +Core* ViewModelComponentBase::clone() const +{ + auto cloned = new ViewModelComponent(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_instance_base.cpp b/src/generated/viewmodel/viewmodel_instance_base.cpp new file mode 100644 index 00000000..892eba08 --- /dev/null +++ b/src/generated/viewmodel/viewmodel_instance_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_instance_base.hpp" +#include "rive/viewmodel/viewmodel_instance.hpp" + +using namespace rive; + +Core* ViewModelInstanceBase::clone() const +{ + auto cloned = new ViewModelInstance(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_instance_boolean_base.cpp b/src/generated/viewmodel/viewmodel_instance_boolean_base.cpp new file mode 100644 index 00000000..641bfecb --- /dev/null +++ b/src/generated/viewmodel/viewmodel_instance_boolean_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_instance_boolean_base.hpp" +#include "rive/viewmodel/viewmodel_instance_boolean.hpp" + +using namespace rive; + +Core* ViewModelInstanceBooleanBase::clone() const +{ + auto cloned = new ViewModelInstanceBoolean(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_instance_color_base.cpp b/src/generated/viewmodel/viewmodel_instance_color_base.cpp new file mode 100644 index 00000000..599a25d4 --- /dev/null +++ b/src/generated/viewmodel/viewmodel_instance_color_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_instance_color_base.hpp" +#include "rive/viewmodel/viewmodel_instance_color.hpp" + +using namespace rive; + +Core* ViewModelInstanceColorBase::clone() const +{ + auto cloned = new ViewModelInstanceColor(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_instance_enum_base.cpp b/src/generated/viewmodel/viewmodel_instance_enum_base.cpp new file mode 100644 index 00000000..3937d48b --- /dev/null +++ b/src/generated/viewmodel/viewmodel_instance_enum_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_instance_enum_base.hpp" +#include "rive/viewmodel/viewmodel_instance_enum.hpp" + +using namespace rive; + +Core* ViewModelInstanceEnumBase::clone() const +{ + auto cloned = new ViewModelInstanceEnum(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_instance_list_base.cpp b/src/generated/viewmodel/viewmodel_instance_list_base.cpp new file mode 100644 index 00000000..b73c5a63 --- /dev/null +++ b/src/generated/viewmodel/viewmodel_instance_list_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_instance_list_base.hpp" +#include "rive/viewmodel/viewmodel_instance_list.hpp" + +using namespace rive; + +Core* ViewModelInstanceListBase::clone() const +{ + auto cloned = new ViewModelInstanceList(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_instance_list_item_base.cpp b/src/generated/viewmodel/viewmodel_instance_list_item_base.cpp new file mode 100644 index 00000000..b327aad5 --- /dev/null +++ b/src/generated/viewmodel/viewmodel_instance_list_item_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_instance_list_item_base.hpp" +#include "rive/viewmodel/viewmodel_instance_list_item.hpp" + +using namespace rive; + +Core* ViewModelInstanceListItemBase::clone() const +{ + auto cloned = new ViewModelInstanceListItem(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_instance_number_base.cpp b/src/generated/viewmodel/viewmodel_instance_number_base.cpp new file mode 100644 index 00000000..6ac41a54 --- /dev/null +++ b/src/generated/viewmodel/viewmodel_instance_number_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_instance_number_base.hpp" +#include "rive/viewmodel/viewmodel_instance_number.hpp" + +using namespace rive; + +Core* ViewModelInstanceNumberBase::clone() const +{ + auto cloned = new ViewModelInstanceNumber(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_instance_string_base.cpp b/src/generated/viewmodel/viewmodel_instance_string_base.cpp new file mode 100644 index 00000000..ce488391 --- /dev/null +++ b/src/generated/viewmodel/viewmodel_instance_string_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_instance_string_base.hpp" +#include "rive/viewmodel/viewmodel_instance_string.hpp" + +using namespace rive; + +Core* ViewModelInstanceStringBase::clone() const +{ + auto cloned = new ViewModelInstanceString(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_instance_viewmodel_base.cpp b/src/generated/viewmodel/viewmodel_instance_viewmodel_base.cpp new file mode 100644 index 00000000..b0854a0c --- /dev/null +++ b/src/generated/viewmodel/viewmodel_instance_viewmodel_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_instance_viewmodel_base.hpp" +#include "rive/viewmodel/viewmodel_instance_viewmodel.hpp" + +using namespace rive; + +Core* ViewModelInstanceViewModelBase::clone() const +{ + auto cloned = new ViewModelInstanceViewModel(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_property_base.cpp b/src/generated/viewmodel/viewmodel_property_base.cpp new file mode 100644 index 00000000..333ffa88 --- /dev/null +++ b/src/generated/viewmodel/viewmodel_property_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_property_base.hpp" +#include "rive/viewmodel/viewmodel_property.hpp" + +using namespace rive; + +Core* ViewModelPropertyBase::clone() const +{ + auto cloned = new ViewModelProperty(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_property_boolean_base.cpp b/src/generated/viewmodel/viewmodel_property_boolean_base.cpp new file mode 100644 index 00000000..0371e8c6 --- /dev/null +++ b/src/generated/viewmodel/viewmodel_property_boolean_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_property_boolean_base.hpp" +#include "rive/viewmodel/viewmodel_property_boolean.hpp" + +using namespace rive; + +Core* ViewModelPropertyBooleanBase::clone() const +{ + auto cloned = new ViewModelPropertyBoolean(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_property_color_base.cpp b/src/generated/viewmodel/viewmodel_property_color_base.cpp new file mode 100644 index 00000000..6f3bf826 --- /dev/null +++ b/src/generated/viewmodel/viewmodel_property_color_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_property_color_base.hpp" +#include "rive/viewmodel/viewmodel_property_color.hpp" + +using namespace rive; + +Core* ViewModelPropertyColorBase::clone() const +{ + auto cloned = new ViewModelPropertyColor(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_property_enum_base.cpp b/src/generated/viewmodel/viewmodel_property_enum_base.cpp new file mode 100644 index 00000000..5f05b710 --- /dev/null +++ b/src/generated/viewmodel/viewmodel_property_enum_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_property_enum_base.hpp" +#include "rive/viewmodel/viewmodel_property_enum.hpp" + +using namespace rive; + +Core* ViewModelPropertyEnumBase::clone() const +{ + auto cloned = new ViewModelPropertyEnum(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_property_list_base.cpp b/src/generated/viewmodel/viewmodel_property_list_base.cpp new file mode 100644 index 00000000..d6a7e79c --- /dev/null +++ b/src/generated/viewmodel/viewmodel_property_list_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_property_list_base.hpp" +#include "rive/viewmodel/viewmodel_property_list.hpp" + +using namespace rive; + +Core* ViewModelPropertyListBase::clone() const +{ + auto cloned = new ViewModelPropertyList(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_property_number_base.cpp b/src/generated/viewmodel/viewmodel_property_number_base.cpp new file mode 100644 index 00000000..bf388200 --- /dev/null +++ b/src/generated/viewmodel/viewmodel_property_number_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_property_number_base.hpp" +#include "rive/viewmodel/viewmodel_property_number.hpp" + +using namespace rive; + +Core* ViewModelPropertyNumberBase::clone() const +{ + auto cloned = new ViewModelPropertyNumber(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_property_string_base.cpp b/src/generated/viewmodel/viewmodel_property_string_base.cpp new file mode 100644 index 00000000..5deac13c --- /dev/null +++ b/src/generated/viewmodel/viewmodel_property_string_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_property_string_base.hpp" +#include "rive/viewmodel/viewmodel_property_string.hpp" + +using namespace rive; + +Core* ViewModelPropertyStringBase::clone() const +{ + auto cloned = new ViewModelPropertyString(); + cloned->copy(*this); + return cloned; +} diff --git a/src/generated/viewmodel/viewmodel_property_viewmodel_base.cpp b/src/generated/viewmodel/viewmodel_property_viewmodel_base.cpp new file mode 100644 index 00000000..5b57217d --- /dev/null +++ b/src/generated/viewmodel/viewmodel_property_viewmodel_base.cpp @@ -0,0 +1,11 @@ +#include "rive/generated/viewmodel/viewmodel_property_viewmodel_base.hpp" +#include "rive/viewmodel/viewmodel_property_viewmodel.hpp" + +using namespace rive; + +Core* ViewModelPropertyViewModelBase::clone() const +{ + auto cloned = new ViewModelPropertyViewModel(); + cloned->copy(*this); + return cloned; +} diff --git a/src/importers/artboard_importer.cpp b/src/importers/artboard_importer.cpp index d0d93df2..f2957b01 100644 --- a/src/importers/artboard_importer.cpp +++ b/src/importers/artboard_importer.cpp @@ -2,6 +2,7 @@ #include "rive/importers/artboard_importer.hpp" #include "rive/animation/linear_animation.hpp" #include "rive/animation/state_machine.hpp" +#include "rive/data_bind/data_bind.hpp" #include "rive/text/text_value_run.hpp" #include "rive/event.hpp" #include "rive/artboard.hpp" @@ -22,6 +23,8 @@ void ArtboardImporter::addStateMachine(StateMachine* stateMachine) m_Artboard->addStateMachine(stateMachine); } +void ArtboardImporter::addDataBind(DataBind* dataBind) { m_Artboard->addDataBind(dataBind); } + StatusCode ArtboardImporter::resolve() { return m_Artboard->initialize(); } bool ArtboardImporter::readNullObject() diff --git a/src/importers/backboard_importer.cpp b/src/importers/backboard_importer.cpp index 19eb762c..97a6c559 100644 --- a/src/importers/backboard_importer.cpp +++ b/src/importers/backboard_importer.cpp @@ -1,8 +1,15 @@ #include "rive/importers/backboard_importer.hpp" +#include "rive/artboard.hpp" #include "rive/nested_artboard.hpp" +#include "rive/backboard.hpp" #include "rive/assets/file_asset_referencer.hpp" #include "rive/assets/file_asset.hpp" +#include "rive/viewmodel/viewmodel.hpp" +#include "rive/viewmodel/viewmodel_instance.hpp" +#include "rive/data_bind/converters/data_converter.hpp" +#include "rive/data_bind/converters/data_converter_group_item.hpp" +#include "rive/data_bind/data_bind.hpp" #include using namespace rive; @@ -59,7 +66,6 @@ void BackboardImporter::addMissingArtboard() { m_NextArtboardId++; } StatusCode BackboardImporter::resolve() { - for (auto nestedArtboard : m_NestedArtboards) { auto itr = m_ArtboardLookup.find(nestedArtboard->artboardId()); @@ -82,6 +88,38 @@ StatusCode BackboardImporter::resolve() auto asset = m_FileAssets[index]; referencer->setAsset(asset); } - + for (auto referencer : m_DataConverterReferencers) + { + auto index = (size_t)referencer->converterId(); + if (index >= m_DataConverters.size() || index < 0) + { + continue; + } + referencer->converter(m_DataConverters[index]); + } + for (auto referencer : m_DataConverterGroupItemReferencers) + { + auto index = (size_t)referencer->converterId(); + if (index >= m_DataConverters.size() || index < 0) + { + continue; + } + referencer->converter(m_DataConverters[index]); + } return StatusCode::Ok; } + +void BackboardImporter::addDataConverter(DataConverter* dataConverter) +{ + m_DataConverters.push_back(dataConverter); +} + +void BackboardImporter::addDataConverterReferencer(DataBind* dataBind) +{ + m_DataConverterReferencers.push_back(dataBind); +} + +void BackboardImporter::addDataConverterGroupItemReferencer(DataConverterGroupItem* dataBind) +{ + m_DataConverterGroupItemReferencers.push_back(dataBind); +} \ No newline at end of file diff --git a/src/importers/bindable_property_importer.cpp b/src/importers/bindable_property_importer.cpp new file mode 100644 index 00000000..1d992afc --- /dev/null +++ b/src/importers/bindable_property_importer.cpp @@ -0,0 +1,9 @@ +#include "rive/artboard.hpp" +#include "rive/importers/bindable_property_importer.hpp" +#include "rive/data_bind/bindable_property.hpp" + +using namespace rive; + +BindablePropertyImporter::BindablePropertyImporter(BindableProperty* bindableProperty) : + m_bindableProperty(bindableProperty) +{} \ No newline at end of file diff --git a/src/importers/data_converter_group_importer.cpp b/src/importers/data_converter_group_importer.cpp new file mode 100644 index 00000000..457a0e2d --- /dev/null +++ b/src/importers/data_converter_group_importer.cpp @@ -0,0 +1,9 @@ +#include "rive/artboard.hpp" +#include "rive/importers/data_converter_group_importer.hpp" +#include "rive/data_bind/converters/data_converter.hpp" + +using namespace rive; + +DataConverterGroupImporter::DataConverterGroupImporter(DataConverterGroup* group) : + m_dataConverterGroup(group) +{} \ No newline at end of file diff --git a/src/importers/enum_importer.cpp b/src/importers/enum_importer.cpp new file mode 100644 index 00000000..65d616a5 --- /dev/null +++ b/src/importers/enum_importer.cpp @@ -0,0 +1,11 @@ +#include "rive/importers/enum_importer.hpp" +#include "rive/viewmodel/data_enum.hpp" +#include "rive/viewmodel/data_enum_value.hpp" + +using namespace rive; + +EnumImporter::EnumImporter(DataEnum* dataEnum) : m_DataEnum(dataEnum) {} + +void EnumImporter::addValue(DataEnumValue* value) { m_DataEnum->addValue(value); } + +StatusCode EnumImporter::resolve() { return StatusCode::Ok; } \ No newline at end of file diff --git a/src/importers/state_machine_importer.cpp b/src/importers/state_machine_importer.cpp index 7994c37b..70a39858 100644 --- a/src/importers/state_machine_importer.cpp +++ b/src/importers/state_machine_importer.cpp @@ -3,6 +3,7 @@ #include "rive/animation/state_machine_listener.hpp" #include "rive/animation/state_machine_input.hpp" #include "rive/animation/state_machine_layer.hpp" +#include "rive/data_bind/data_bind.hpp" using namespace rive; @@ -23,6 +24,11 @@ void StateMachineImporter::addListener(std::unique_ptr lis m_StateMachine->addListener(std::move(listener)); } +void StateMachineImporter::addDataBind(std::unique_ptr dataBind) +{ + m_StateMachine->addDataBind(std::move(dataBind)); +} + bool StateMachineImporter::readNullObject() { // Hard assumption that we won't add new layer types... diff --git a/src/importers/transition_viewmodel_condition_importer.cpp b/src/importers/transition_viewmodel_condition_importer.cpp new file mode 100644 index 00000000..97d4cb0a --- /dev/null +++ b/src/importers/transition_viewmodel_condition_importer.cpp @@ -0,0 +1,16 @@ +#include "rive/importers/transition_viewmodel_condition_importer.hpp" +#include "rive/animation/transition_viewmodel_condition.hpp" +#include "rive/animation/transition_comparator.hpp" +#include "rive/data_bind/data_bind.hpp" + +using namespace rive; + +TransitionViewModelConditionImporter::TransitionViewModelConditionImporter( + TransitionViewModelCondition* transitionViewModelCondition) : + m_TransitionViewModelCondition(transitionViewModelCondition) +{} + +void TransitionViewModelConditionImporter::setComparator(TransitionComparator* comparator) +{ + m_TransitionViewModelCondition->comparator(comparator); +} \ No newline at end of file diff --git a/src/importers/viewmodel_importer.cpp b/src/importers/viewmodel_importer.cpp new file mode 100644 index 00000000..d868d052 --- /dev/null +++ b/src/importers/viewmodel_importer.cpp @@ -0,0 +1,15 @@ +#include "rive/importers/viewmodel_importer.hpp" +#include "rive/viewmodel/viewmodel.hpp" +#include "rive/viewmodel/viewmodel_property.hpp" +#include "rive/viewmodel/viewmodel_instance.hpp" + +using namespace rive; + +ViewModelImporter::ViewModelImporter(ViewModel* viewModel) : m_ViewModel(viewModel) {} +void ViewModelImporter::addProperty(ViewModelProperty* property) +{ + m_ViewModel->addProperty(property); +} +void ViewModelImporter::addInstance(ViewModelInstance* value) { m_ViewModel->addInstance(value); } + +StatusCode ViewModelImporter::resolve() { return StatusCode::Ok; } \ No newline at end of file diff --git a/src/importers/viewmodel_instance_importer.cpp b/src/importers/viewmodel_instance_importer.cpp new file mode 100644 index 00000000..c4642fc0 --- /dev/null +++ b/src/importers/viewmodel_instance_importer.cpp @@ -0,0 +1,15 @@ +#include "rive/importers/viewmodel_instance_importer.hpp" +#include "rive/viewmodel/viewmodel_instance.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" + +using namespace rive; + +ViewModelInstanceImporter::ViewModelInstanceImporter(ViewModelInstance* viewModelInstance) : + m_ViewModelInstance(viewModelInstance) +{} +void ViewModelInstanceImporter::addValue(ViewModelInstanceValue* value) +{ + m_ViewModelInstance->addValue(value); +} + +StatusCode ViewModelInstanceImporter::resolve() { return StatusCode::Ok; } \ No newline at end of file diff --git a/src/importers/viewmodel_instance_list_importer.cpp b/src/importers/viewmodel_instance_list_importer.cpp new file mode 100644 index 00000000..2edf0625 --- /dev/null +++ b/src/importers/viewmodel_instance_list_importer.cpp @@ -0,0 +1,16 @@ +#include "rive/importers/viewmodel_instance_list_importer.hpp" +#include "rive/viewmodel/viewmodel_instance_list.hpp" +#include "rive/viewmodel/viewmodel_instance_list_item.hpp" + +using namespace rive; + +ViewModelInstanceListImporter::ViewModelInstanceListImporter( + ViewModelInstanceList* viewModelInstanceList) : + m_ViewModelInstanceList(viewModelInstanceList) +{} +void ViewModelInstanceListImporter::addItem(ViewModelInstanceListItem* listItem) +{ + m_ViewModelInstanceList->addItem(listItem); +} + +StatusCode ViewModelInstanceListImporter::resolve() { return StatusCode::Ok; } \ No newline at end of file diff --git a/src/layout/layout_component_style.cpp b/src/layout/layout_component_style.cpp new file mode 100644 index 00000000..504a5bb0 --- /dev/null +++ b/src/layout/layout_component_style.cpp @@ -0,0 +1,215 @@ +#include "rive/animation/keyframe_interpolator.hpp" +#include "rive/core_context.hpp" +#include "rive/layout_component.hpp" +#include "rive/layout/layout_component_style.hpp" +#include + +using namespace rive; + +#ifdef WITH_RIVE_LAYOUT + +KeyFrameInterpolator* LayoutComponentStyle::interpolator() { return m_interpolator; } + +LayoutStyleInterpolation LayoutComponentStyle::interpolation() +{ + return LayoutStyleInterpolation(interpolationType()); +} + +LayoutAnimationStyle LayoutComponentStyle::animationStyle() +{ + return LayoutAnimationStyle(animationStyleType()); +} + +LayoutAlignmentType LayoutComponentStyle::alignmentType() +{ + return LayoutAlignmentType(layoutAlignmentType()); +} + +LayoutScaleType LayoutComponentStyle::widthScaleType() +{ + return LayoutScaleType(layoutWidthScaleType()); +} + +LayoutScaleType LayoutComponentStyle::heightScaleType() +{ + return LayoutScaleType(layoutHeightScaleType()); +} + +YGDisplay LayoutComponentStyle::display() { return YGDisplay(displayValue()); } + +YGPositionType LayoutComponentStyle::positionType() { return YGPositionType(positionTypeValue()); } + +YGFlexDirection LayoutComponentStyle::flexDirection() +{ + return YGFlexDirection(flexDirectionValue()); +} + +YGDirection LayoutComponentStyle::direction() { return YGDirection(directionValue()); } + +YGWrap LayoutComponentStyle::flexWrap() { return YGWrap(flexWrapValue()); } + +YGAlign LayoutComponentStyle::alignItems() { return YGAlign(alignItemsValue()); } + +YGAlign LayoutComponentStyle::alignSelf() { return YGAlign(alignSelfValue()); } + +YGAlign LayoutComponentStyle::alignContent() { return YGAlign(alignContentValue()); } + +YGJustify LayoutComponentStyle::justifyContent() { return YGJustify(justifyContentValue()); } + +YGOverflow LayoutComponentStyle::overflow() { return YGOverflow(overflowValue()); } + +bool LayoutComponentStyle::intrinsicallySized() { return intrinsicallySizedValue() == 1; } + +YGUnit LayoutComponentStyle::widthUnits() { return YGUnit(widthUnitsValue()); } + +YGUnit LayoutComponentStyle::heightUnits() { return YGUnit(heightUnitsValue()); } + +YGUnit LayoutComponentStyle::borderLeftUnits() { return YGUnit(borderLeftUnitsValue()); } + +YGUnit LayoutComponentStyle::borderRightUnits() { return YGUnit(borderRightUnitsValue()); } + +YGUnit LayoutComponentStyle::borderTopUnits() { return YGUnit(borderTopUnitsValue()); } + +YGUnit LayoutComponentStyle::borderBottomUnits() { return YGUnit(borderBottomUnitsValue()); } + +YGUnit LayoutComponentStyle::marginLeftUnits() { return YGUnit(marginLeftUnitsValue()); } + +YGUnit LayoutComponentStyle::marginRightUnits() { return YGUnit(marginRightUnitsValue()); } + +YGUnit LayoutComponentStyle::marginTopUnits() { return YGUnit(marginTopUnitsValue()); } + +YGUnit LayoutComponentStyle::marginBottomUnits() { return YGUnit(marginBottomUnitsValue()); } + +YGUnit LayoutComponentStyle::paddingLeftUnits() { return YGUnit(paddingLeftUnitsValue()); } + +YGUnit LayoutComponentStyle::paddingRightUnits() { return YGUnit(paddingRightUnitsValue()); } + +YGUnit LayoutComponentStyle::paddingTopUnits() { return YGUnit(paddingTopUnitsValue()); } + +YGUnit LayoutComponentStyle::paddingBottomUnits() { return YGUnit(paddingBottomUnitsValue()); } + +YGUnit LayoutComponentStyle::positionLeftUnits() { return YGUnit(positionLeftUnitsValue()); } + +YGUnit LayoutComponentStyle::positionRightUnits() { return YGUnit(positionRightUnitsValue()); } + +YGUnit LayoutComponentStyle::positionTopUnits() { return YGUnit(positionTopUnitsValue()); } + +YGUnit LayoutComponentStyle::positionBottomUnits() { return YGUnit(positionBottomUnitsValue()); } + +YGUnit LayoutComponentStyle::gapHorizontalUnits() { return YGUnit(gapHorizontalUnitsValue()); } + +YGUnit LayoutComponentStyle::gapVerticalUnits() { return YGUnit(gapVerticalUnitsValue()); } + +YGUnit LayoutComponentStyle::maxWidthUnits() { return YGUnit(maxWidthUnitsValue()); } + +YGUnit LayoutComponentStyle::maxHeightUnits() { return YGUnit(maxHeightUnitsValue()); } + +YGUnit LayoutComponentStyle::minWidthUnits() { return YGUnit(minWidthUnitsValue()); } +YGUnit LayoutComponentStyle::minHeightUnits() { return YGUnit(minHeightUnitsValue()); } +void LayoutComponentStyle::markLayoutNodeDirty() +{ + if (parent()->is()) + { + parent()->as()->markLayoutNodeDirty(); + } +} + +void LayoutComponentStyle::markLayoutStyleDirty() +{ + if (parent()->is()) + { + parent()->as()->markLayoutStyleDirty(); + } +} + +StatusCode LayoutComponentStyle::onAddedDirty(CoreContext* context) +{ + auto code = Super::onAddedDirty(context); + if (code != StatusCode::Ok) + { + return code; + } + + auto coreObject = context->resolve(interpolatorId()); + if (coreObject != nullptr && coreObject->is()) + { + m_interpolator = static_cast(coreObject); + } + return StatusCode::Ok; +} +#else +void LayoutComponentStyle::markLayoutNodeDirty() {} +void LayoutComponentStyle::markLayoutStyleDirty() {} +#endif + +void LayoutComponentStyle::layoutAlignmentTypeChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::layoutWidthScaleTypeChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::layoutHeightScaleTypeChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::displayValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::positionTypeValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::overflowValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::intrinsicallySizedValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::flexDirectionValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::directionValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::alignContentValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::alignItemsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::alignSelfValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::justifyContentValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::flexWrapValueChanged() { markLayoutNodeDirty(); } + +void LayoutComponentStyle::flexChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::flexGrowChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::flexShrinkChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::flexBasisChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::aspectRatioChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::gapHorizontalChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::gapVerticalChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::maxWidthChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::maxHeightChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::minWidthChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::minHeightChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::borderLeftChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::borderRightChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::borderTopChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::borderBottomChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::marginLeftChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::marginRightChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::marginTopChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::marginBottomChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::paddingLeftChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::paddingRightChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::paddingTopChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::paddingBottomChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::positionLeftChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::positionRightChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::positionTopChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::positionBottomChanged() { markLayoutNodeDirty(); } + +void LayoutComponentStyle::widthUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::heightUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::gapHorizontalUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::gapVerticalUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::maxWidthUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::maxHeightUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::minWidthUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::minHeightUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::borderLeftUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::borderRightUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::borderTopUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::borderBottomUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::marginLeftUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::marginRightUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::marginTopUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::marginBottomUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::paddingLeftUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::paddingRightUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::paddingTopUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::paddingBottomUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::positionLeftUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::positionRightUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::positionTopUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::positionBottomUnitsValueChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::cornerRadiusTLChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::cornerRadiusTRChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::cornerRadiusBLChanged() { markLayoutNodeDirty(); } +void LayoutComponentStyle::cornerRadiusBRChanged() { markLayoutNodeDirty(); } \ No newline at end of file diff --git a/src/layout_component.cpp b/src/layout_component.cpp new file mode 100644 index 00000000..c72a6140 --- /dev/null +++ b/src/layout_component.cpp @@ -0,0 +1,877 @@ +#include "rive/animation/keyframe_interpolator.hpp" +#include "rive/artboard.hpp" +#include "rive/drawable.hpp" +#include "rive/factory.hpp" +#include "rive/layout_component.hpp" +#include "rive/node.hpp" +#include "rive/math/aabb.hpp" +#include "rive/shapes/paint/fill.hpp" +#include "rive/shapes/paint/shape_paint.hpp" +#include "rive/shapes/paint/stroke.hpp" +#include "rive/shapes/rectangle.hpp" +#include "rive/nested_artboard_layout.hpp" +#ifdef WITH_RIVE_LAYOUT +#include "rive/transform_component.hpp" +#include "yoga/YGEnums.h" +#include "yoga/YGFloatOptional.h" +#endif +#include + +using namespace rive; + +void LayoutComponent::buildDependencies() +{ + Super::buildDependencies(); + if (parent() != nullptr) + { + parent()->addDependent(this); + } + // Set the blend mode on all the shape paints. If we ever animate this + // property, we'll need to update it in the update cycle/mark dirty when the + // blend mode changes. + for (auto paint : m_ShapePaints) + { + paint->blendMode(blendMode()); + } +} + +void LayoutComponent::drawProxy(Renderer* renderer) +{ + if (clip()) + { + renderer->save(); + renderer->clipPath(m_clipPath.get()); + } + renderer->save(); + renderer->transform(worldTransform()); + for (auto shapePaint : m_ShapePaints) + { + if (!shapePaint->isVisible()) + { + continue; + } + if (shapePaint->is()) + { + shapePaint->draw(renderer, m_backgroundPath.get(), &m_backgroundRect->rawPath()); + } + } + renderer->restore(); +} + +void LayoutComponent::draw(Renderer* renderer) +{ + // Restore clip before drawing stroke so we don't clip the stroke + if (clip()) + { + renderer->restore(); + } + renderer->save(); + renderer->transform(worldTransform()); + for (auto shapePaint : m_ShapePaints) + { + if (!shapePaint->isVisible()) + { + continue; + } + if (shapePaint->is()) + { + shapePaint->draw(renderer, m_backgroundPath.get(), &m_backgroundRect->rawPath()); + } + } + renderer->restore(); +} + +Core* LayoutComponent::hitTest(HitInfo*, const Mat2D&) { return nullptr; } + +void LayoutComponent::updateRenderPath() +{ + m_backgroundRect->width(m_layoutSizeWidth); + m_backgroundRect->height(m_layoutSizeHeight); + m_backgroundRect->linkCornerRadius(style()->linkCornerRadius()); + m_backgroundRect->cornerRadiusTL(style()->cornerRadiusTL()); + m_backgroundRect->cornerRadiusTR(style()->cornerRadiusTR()); + m_backgroundRect->cornerRadiusBL(style()->cornerRadiusBL()); + m_backgroundRect->cornerRadiusBR(style()->cornerRadiusBR()); + m_backgroundRect->update(ComponentDirt::Path); + + m_backgroundPath->rewind(); + m_backgroundRect->rawPath().addTo(m_backgroundPath.get()); + + RawPath clipPath; + clipPath.addPath(m_backgroundRect->rawPath(), &m_WorldTransform); + m_clipPath = artboard()->factory()->makeRenderPath(clipPath, FillRule::nonZero); +} + +void LayoutComponent::update(ComponentDirt value) +{ + Super::update(value); + if (hasDirt(value, ComponentDirt::RenderOpacity)) + { + propagateOpacity(childOpacity()); + } + if (parent() != nullptr && hasDirt(value, ComponentDirt::WorldTransform)) + { + Mat2D parentWorld = parent()->is() + ? (parent()->as())->worldTransform() + : Mat2D(); + auto location = Vec2D(m_layoutLocationX, m_layoutLocationY); + if (parent()->is()) + { + auto art = parent()->as(); + location -= + Vec2D(art->layoutWidth() * art->originX(), art->layoutHeight() * art->originY()); + } + auto transform = Mat2D::fromTranslation(location); + m_WorldTransform = Mat2D::multiply(parentWorld, transform); + updateConstraints(); + } + if (hasDirt(value, ComponentDirt::Path)) + { + updateRenderPath(); + } +} + +void LayoutComponent::widthOverride(float width, int unitValue, bool isRow) +{ + m_widthOverride = width; + m_widthUnitValueOverride = unitValue; + m_parentIsRow = isRow; + markLayoutNodeDirty(); +} + +void LayoutComponent::heightOverride(float height, int unitValue, bool isRow) +{ + m_heightOverride = height; + m_heightUnitValueOverride = unitValue; + m_parentIsRow = isRow; + markLayoutNodeDirty(); +} + +#ifdef WITH_RIVE_LAYOUT +StatusCode LayoutComponent::onAddedDirty(CoreContext* context) +{ + auto code = Super::onAddedDirty(context); + if (code != StatusCode::Ok) + { + return code; + } + + auto coreStyle = context->resolve(styleId()); + if (coreStyle == nullptr || !coreStyle->is()) + { + return StatusCode::MissingObject; + } + m_style = static_cast(coreStyle); + addChild(m_style); + + return StatusCode::Ok; +} + +StatusCode LayoutComponent::onAddedClean(CoreContext* context) +{ + auto code = Super::onAddedClean(context); + if (code != StatusCode::Ok) + { + return code; + } + artboard()->markLayoutDirty(this); + markLayoutStyleDirty(); + m_backgroundPath = artboard()->factory()->makeEmptyRenderPath(); + m_clipPath = artboard()->factory()->makeEmptyRenderPath(); + m_backgroundRect->originX(0); + m_backgroundRect->originY(0); + syncLayoutChildren(); + return StatusCode::Ok; +} + +static YGSize measureFunc(YGNode* node, + float width, + YGMeasureMode widthMode, + float height, + YGMeasureMode heightMode) +{ + Vec2D size = ((LayoutComponent*)node->getContext()) + ->measureLayout(width, + (LayoutMeasureMode)widthMode, + height, + (LayoutMeasureMode)heightMode); + + return YGSize{size.x, size.y}; +} + +Vec2D LayoutComponent::measureLayout(float width, + LayoutMeasureMode widthMode, + float height, + LayoutMeasureMode heightMode) +{ + Vec2D size = Vec2D(); + for (auto child : children()) + { + if (child->is()) + { + continue; + } + // && child->is()->canMeasure() for nested artboard layout + if (child->is()) + { + auto transformComponent = child->as(); + Vec2D measured = + transformComponent->measureLayout(width, widthMode, height, heightMode); + size = Vec2D(std::max(size.x, measured.x), std::max(size.y, measured.y)); + } + } + return size; +} + +bool LayoutComponent::mainAxisIsRow() +{ + return style()->flexDirection() == YGFlexDirectionRow || + style()->flexDirection() == YGFlexDirectionRowReverse; +} + +bool LayoutComponent::mainAxisIsColumn() +{ + return style()->flexDirection() == YGFlexDirectionColumn || + style()->flexDirection() == YGFlexDirectionColumnReverse; +} + +void LayoutComponent::syncStyle() +{ + if (m_style == nullptr) + { + return; + } + YGNode& ygNode = layoutNode(); + YGStyle& ygStyle = layoutStyle(); + if (m_style->intrinsicallySized()) + { + ygNode.setContext(this); + ygNode.setMeasureFunc(measureFunc); + } + else + { + ygNode.setMeasureFunc(nullptr); + } + + auto realWidth = width(); + auto realWidthUnits = m_style->widthUnits(); + auto realWidthScaleType = m_style->widthScaleType(); + auto realHeight = height(); + auto realHeightUnits = m_style->heightUnits(); + auto realHeightScaleType = m_style->heightScaleType(); + auto parentIsRow = layoutParent() != nullptr ? layoutParent()->mainAxisIsRow() : true; + + // If we have override width/height values, use those. + // Currently we only use these for Artboards that are part of a NestedArtboardLayout + // but perhaps there will be other use cases for overriding in the future? + if (canHaveOverrides()) + { + if (!std::isnan(m_widthOverride)) + { + realWidth = m_widthOverride; + } + if (!std::isnan(m_heightOverride)) + { + realHeight = m_heightOverride; + } + parentIsRow = m_parentIsRow; + + if (m_widthUnitValueOverride != -1) + { + realWidthUnits = YGUnit(m_widthUnitValueOverride); + switch (realWidthUnits) + { + case YGUnitPoint: + case YGUnitPercent: + realWidthScaleType = LayoutScaleType::fixed; + break; + case YGUnitAuto: + realWidthScaleType = LayoutScaleType::fill; + break; + default: + break; + } + } + if (m_heightUnitValueOverride != -1) + { + realHeightUnits = YGUnit(m_heightUnitValueOverride); + switch (realHeightUnits) + { + case YGUnitPoint: + case YGUnitPercent: + realHeightScaleType = LayoutScaleType::fixed; + break; + case YGUnitAuto: + realHeightScaleType = LayoutScaleType::fill; + break; + default: + break; + } + } + } + ygStyle.dimensions()[YGDimensionWidth] = YGValue{realWidth, realWidthUnits}; + ygStyle.dimensions()[YGDimensionHeight] = YGValue{realHeight, realHeightUnits}; + + switch (realWidthScaleType) + { + case LayoutScaleType::fixed: + if (parentIsRow) + { + ygStyle.flexGrow() = YGFloatOptional(0); + } + break; + case LayoutScaleType::fill: + if (parentIsRow) + { + ygStyle.flexGrow() = YGFloatOptional(1); + } + else + { + ygStyle.alignSelf() = YGAlignStretch; + } + break; + case LayoutScaleType::hug: + if (parentIsRow) + { + ygStyle.flexGrow() = YGFloatOptional(0); + } + else + { + ygStyle.alignSelf() = YGAlignAuto; + } + break; + default: + break; + } + + switch (realHeightScaleType) + { + case LayoutScaleType::fixed: + if (!parentIsRow) + { + ygStyle.flexGrow() = YGFloatOptional(0); + } + break; + case LayoutScaleType::fill: + if (!parentIsRow) + { + ygStyle.flexGrow() = YGFloatOptional(1); + } + else + { + ygStyle.alignSelf() = YGAlignStretch; + } + break; + case LayoutScaleType::hug: + if (!parentIsRow) + { + ygStyle.flexGrow() = YGFloatOptional(0); + } + else + { + ygStyle.alignSelf() = YGAlignAuto; + } + break; + default: + break; + } + + bool isRowForAlignment = mainAxisIsRow(); + switch (m_style->alignmentType()) + { + case LayoutAlignmentType::topLeft: + case LayoutAlignmentType::topCenter: + case LayoutAlignmentType::topRight: + case LayoutAlignmentType::spaceBetweenStart: + if (isRowForAlignment) + { + ygStyle.alignItems() = YGAlignFlexStart; + ygStyle.alignContent() = YGAlignFlexStart; + } + else + { + ygStyle.justifyContent() = YGJustifyFlexStart; + } + break; + case LayoutAlignmentType::centerLeft: + case LayoutAlignmentType::center: + case LayoutAlignmentType::centerRight: + case LayoutAlignmentType::spaceBetweenCenter: + if (isRowForAlignment) + { + ygStyle.alignItems() = YGAlignCenter; + ygStyle.alignContent() = YGAlignCenter; + } + else + { + ygStyle.justifyContent() = YGJustifyCenter; + } + break; + case LayoutAlignmentType::bottomLeft: + case LayoutAlignmentType::bottomCenter: + case LayoutAlignmentType::bottomRight: + case LayoutAlignmentType::spaceBetweenEnd: + if (isRowForAlignment) + { + ygStyle.alignItems() = YGAlignFlexEnd; + ygStyle.alignContent() = YGAlignFlexEnd; + } + else + { + ygStyle.justifyContent() = YGJustifyFlexEnd; + } + break; + } + switch (m_style->alignmentType()) + { + case LayoutAlignmentType::topLeft: + case LayoutAlignmentType::centerLeft: + case LayoutAlignmentType::bottomLeft: + if (isRowForAlignment) + { + ygStyle.justifyContent() = YGJustifyFlexStart; + } + else + { + ygStyle.alignItems() = YGAlignFlexStart; + ygStyle.alignContent() = YGAlignFlexStart; + } + break; + case LayoutAlignmentType::topCenter: + case LayoutAlignmentType::center: + case LayoutAlignmentType::bottomCenter: + if (isRowForAlignment) + { + ygStyle.justifyContent() = YGJustifyCenter; + } + else + { + ygStyle.alignItems() = YGAlignCenter; + ygStyle.alignContent() = YGAlignCenter; + } + break; + case LayoutAlignmentType::topRight: + case LayoutAlignmentType::centerRight: + case LayoutAlignmentType::bottomRight: + if (isRowForAlignment) + { + ygStyle.justifyContent() = YGJustifyFlexEnd; + } + else + { + ygStyle.alignItems() = YGAlignFlexEnd; + ygStyle.alignContent() = YGAlignFlexEnd; + } + break; + case LayoutAlignmentType::spaceBetweenStart: + case LayoutAlignmentType::spaceBetweenCenter: + case LayoutAlignmentType::spaceBetweenEnd: + ygStyle.justifyContent() = YGJustifySpaceBetween; + break; + } + + ygStyle.minDimensions()[YGDimensionWidth] = + YGValue{m_style->minWidth(), m_style->minWidthUnits()}; + ygStyle.minDimensions()[YGDimensionHeight] = + YGValue{m_style->minHeight(), m_style->minHeightUnits()}; + ygStyle.maxDimensions()[YGDimensionWidth] = + YGValue{m_style->maxWidth(), m_style->maxWidthUnits()}; + ygStyle.maxDimensions()[YGDimensionHeight] = + YGValue{m_style->maxHeight(), m_style->maxHeightUnits()}; + ygStyle.gap()[YGGutterColumn] = + YGValue{m_style->gapHorizontal(), m_style->gapHorizontalUnits()}; + ygStyle.gap()[YGGutterRow] = YGValue{m_style->gapVertical(), m_style->gapVerticalUnits()}; + ygStyle.border()[YGEdgeLeft] = YGValue{m_style->borderLeft(), m_style->borderLeftUnits()}; + ygStyle.border()[YGEdgeRight] = YGValue{m_style->borderRight(), m_style->borderRightUnits()}; + ygStyle.border()[YGEdgeTop] = YGValue{m_style->borderTop(), m_style->borderTopUnits()}; + ygStyle.border()[YGEdgeBottom] = YGValue{m_style->borderBottom(), m_style->borderBottomUnits()}; + ygStyle.margin()[YGEdgeLeft] = YGValue{m_style->marginLeft(), m_style->marginLeftUnits()}; + ygStyle.margin()[YGEdgeRight] = YGValue{m_style->marginRight(), m_style->marginRightUnits()}; + ygStyle.margin()[YGEdgeTop] = YGValue{m_style->marginTop(), m_style->marginTopUnits()}; + ygStyle.margin()[YGEdgeBottom] = YGValue{m_style->marginBottom(), m_style->marginBottomUnits()}; + ygStyle.padding()[YGEdgeLeft] = YGValue{m_style->paddingLeft(), m_style->paddingLeftUnits()}; + ygStyle.padding()[YGEdgeRight] = YGValue{m_style->paddingRight(), m_style->paddingRightUnits()}; + ygStyle.padding()[YGEdgeTop] = YGValue{m_style->paddingTop(), m_style->paddingTopUnits()}; + ygStyle.padding()[YGEdgeBottom] = + YGValue{m_style->paddingBottom(), m_style->paddingBottomUnits()}; + ygStyle.position()[YGEdgeLeft] = YGValue{m_style->positionLeft(), m_style->positionLeftUnits()}; + ygStyle.position()[YGEdgeRight] = + YGValue{m_style->positionRight(), m_style->positionRightUnits()}; + ygStyle.position()[YGEdgeTop] = YGValue{m_style->positionTop(), m_style->positionTopUnits()}; + ygStyle.position()[YGEdgeBottom] = + YGValue{m_style->positionBottom(), m_style->positionBottomUnits()}; + + ygStyle.display() = m_style->display(); + ygStyle.positionType() = m_style->positionType(); + ygStyle.flex() = YGFloatOptional(m_style->flex()); + ygStyle.flexDirection() = m_style->flexDirection(); + ygStyle.flexWrap() = m_style->flexWrap(); + + ygNode.setStyle(ygStyle); +} + +void LayoutComponent::syncLayoutChildren() +{ + auto ourNode = &layoutNode(); + YGNodeRemoveAllChildren(ourNode); + int index = 0; + for (auto child : children()) + { + YGNode* node = nullptr; + switch (child->coreType()) + { + case LayoutComponentBase::typeKey: + node = &child->as()->layoutNode(); + break; + case NestedArtboardLayoutBase::typeKey: + node = static_cast(child->as()->layoutNode()); + break; + } + if (node != nullptr) + { + // YGNodeInsertChild(ourNode, node, index++); + ourNode->insertChild(node, index++); + node->setOwner(ourNode); + ourNode->markDirtyAndPropagate(); + } + } + markLayoutNodeDirty(); +} + +void LayoutComponent::propagateSize() { propagateSizeToChildren(this); } + +void LayoutComponent::propagateSizeToChildren(ContainerComponent* component) +{ + for (auto child : component->children()) + { + if (child->is() || child->coreType() == NodeBase::typeKey) + { + continue; + } + if (child->is()) + { + auto sizableChild = child->as(); + sizableChild->controlSize(Vec2D(m_layoutSizeWidth, m_layoutSizeHeight)); + } + if (child->is()) + { + propagateSizeToChildren(child->as()); + } + } +} + +void LayoutComponent::calculateLayout() +{ + YGNodeCalculateLayout(&layoutNode(), width(), height(), YGDirection::YGDirectionInherit); +} + +void LayoutComponent::onDirty(ComponentDirt value) +{ + Super::onDirty(value); + if ((value & ComponentDirt::WorldTransform) == ComponentDirt::WorldTransform && clip()) + { + addDirt(ComponentDirt::Path); + } +} + +void LayoutComponent::updateLayoutBounds() +{ + auto node = &layoutNode(); + auto left = YGNodeLayoutGetLeft(node); + auto top = YGNodeLayoutGetTop(node); + auto width = YGNodeLayoutGetWidth(node); + auto height = YGNodeLayoutGetHeight(node); + +#ifdef DEBUG + // Temporarily here to keep track of an issue. + if (left != left || top != top || width != width || height != height) + { + fprintf(stderr, + "Layout returned nan: %f %f %f %f | %p %s\n", + left, + top, + width, + height, + YGNodeGetParent(node), + name().c_str()); + return; + } +#endif + if (animates()) + { + auto toBounds = m_animationData.toBounds; + if (left != toBounds.left() || top != toBounds.top() || width != toBounds.width() || + height != toBounds.height()) + { + m_animationData.fromBounds = AABB(m_layoutLocationX, + m_layoutLocationY, + m_layoutLocationX + this->width(), + m_layoutLocationY + this->height()); + m_animationData.toBounds = AABB(left, top, left + width, top + height); + if (m_animationData.elapsedSeconds > 0.1) + { + m_animationData.elapsedSeconds = 0; + } + propagateSize(); + markWorldTransformDirty(); + } + } + else + + if (left != m_layoutLocationX || top != m_layoutLocationY || width != m_layoutSizeWidth || + height != m_layoutSizeHeight) + { + if (m_layoutSizeWidth != width || m_layoutSizeHeight != height) + { + // Width changed, we need to rebuild the path. + addDirt(ComponentDirt::Path); + } + m_layoutLocationX = left; + m_layoutLocationY = top; + m_layoutSizeWidth = width; + m_layoutSizeHeight = height; + + propagateSize(); + markWorldTransformDirty(); + } +} + +bool LayoutComponent::advance(double elapsedSeconds) { return applyInterpolation(elapsedSeconds); } + +bool LayoutComponent::animates() +{ + if (m_style == nullptr) + { + return false; + } + return m_style->positionType() == YGPositionType::YGPositionTypeRelative && + m_style->animationStyle() != LayoutAnimationStyle::none && + interpolation() != LayoutStyleInterpolation::hold && interpolationTime() > 0; +} + +LayoutAnimationStyle LayoutComponent::animationStyle() +{ + if (m_style == nullptr) + { + return LayoutAnimationStyle::none; + } + return m_style->animationStyle(); +} + +KeyFrameInterpolator* LayoutComponent::interpolator() +{ + if (m_style == nullptr) + { + return nullptr; + } + switch (m_style->animationStyle()) + { + case LayoutAnimationStyle::inherit: + return m_inheritedInterpolator != nullptr ? m_inheritedInterpolator + : m_style->interpolator(); + case LayoutAnimationStyle::custom: + return m_style->interpolator(); + default: + return nullptr; + } +} + +LayoutStyleInterpolation LayoutComponent::interpolation() +{ + auto defaultInterpolation = LayoutStyleInterpolation::hold; + if (m_style == nullptr) + { + return defaultInterpolation; + } + switch (m_style->animationStyle()) + { + case LayoutAnimationStyle::inherit: + return m_inheritedInterpolation; + case LayoutAnimationStyle::custom: + return m_style->interpolation(); + default: + return defaultInterpolation; + } +} + +float LayoutComponent::interpolationTime() +{ + if (m_style == nullptr) + { + return 0; + } + switch (m_style->animationStyle()) + { + case LayoutAnimationStyle::inherit: + return m_inheritedInterpolationTime; + case LayoutAnimationStyle::custom: + return m_style->interpolationTime(); + default: + return 0; + } +} + +void LayoutComponent::cascadeAnimationStyle(LayoutStyleInterpolation inheritedInterpolation, + KeyFrameInterpolator* inheritedInterpolator, + float inheritedInterpolationTime) +{ + if (m_style != nullptr && m_style->animationStyle() == LayoutAnimationStyle::inherit) + { + setInheritedInterpolation(inheritedInterpolation, + inheritedInterpolator, + inheritedInterpolationTime); + } + else + { + clearInheritedInterpolation(); + } + for (auto child : children()) + { + if (child->is()) + { + child->as()->cascadeAnimationStyle(interpolation(), + interpolator(), + interpolationTime()); + } + } +} + +void LayoutComponent::setInheritedInterpolation(LayoutStyleInterpolation inheritedInterpolation, + KeyFrameInterpolator* inheritedInterpolator, + float inheritedInterpolationTime) +{ + m_inheritedInterpolation = inheritedInterpolation; + m_inheritedInterpolator = inheritedInterpolator; + m_inheritedInterpolationTime = inheritedInterpolationTime; +} + +void LayoutComponent::clearInheritedInterpolation() +{ + m_inheritedInterpolation = LayoutStyleInterpolation::hold; + m_inheritedInterpolator = nullptr; + m_inheritedInterpolationTime = 0; +} + +bool LayoutComponent::applyInterpolation(double elapsedSeconds) +{ + if (!animates() || m_style == nullptr || m_animationData.toBounds == layoutBounds()) + { + return false; + } + + if (m_animationData.elapsedSeconds >= interpolationTime()) + { + m_layoutLocationX = m_animationData.toBounds.left(); + m_layoutLocationY = m_animationData.toBounds.top(); + + float width = m_animationData.toBounds.width(); + float height = m_animationData.toBounds.height(); + if (width != m_layoutSizeWidth || height != m_layoutSizeHeight) + { + addDirt(ComponentDirt::Path); + } + m_layoutSizeWidth = width; + m_layoutSizeHeight = height; + + m_animationData.elapsedSeconds = 0; + propagateSize(); + markWorldTransformDirty(); + + return false; + } + float f = 1; + if (interpolationTime() > 0) + { + f = m_animationData.elapsedSeconds / interpolationTime(); + } + bool needsAdvance = false; + auto fromBounds = m_animationData.fromBounds; + auto toBounds = m_animationData.toBounds; + auto left = m_layoutLocationX; + auto top = m_layoutLocationY; + auto width = m_layoutSizeWidth; + auto height = m_layoutSizeHeight; + if (toBounds.left() != left || toBounds.top() != top) + { + if (interpolation() == LayoutStyleInterpolation::linear) + { + left = fromBounds.left() + f * (toBounds.left() - fromBounds.left()); + top = fromBounds.top() + f * (toBounds.top() - fromBounds.top()); + } + else + { + if (interpolator() != nullptr) + { + left = interpolator()->transformValue(fromBounds.left(), toBounds.left(), f); + top = interpolator()->transformValue(fromBounds.top(), toBounds.top(), f); + } + } + needsAdvance = true; + m_layoutLocationX = left; + m_layoutLocationY = top; + } + if (toBounds.width() != width || toBounds.height() != height) + { + if (interpolation() == LayoutStyleInterpolation::linear) + { + width = fromBounds.width() + f * (toBounds.width() - fromBounds.width()); + height = fromBounds.height() + f * (toBounds.height() - fromBounds.height()); + } + else + { + if (interpolator() != nullptr) + { + width = interpolator()->transformValue(fromBounds.width(), toBounds.width(), f); + height = interpolator()->transformValue(fromBounds.height(), toBounds.height(), f); + } + } + needsAdvance = true; + m_layoutSizeWidth = width; + m_layoutSizeHeight = height; + addDirt(ComponentDirt::Path); + } + m_animationData.elapsedSeconds = m_animationData.elapsedSeconds + (float)elapsedSeconds; + if (needsAdvance) + { + propagateSize(); + markWorldTransformDirty(); + } + return needsAdvance; +} + +void LayoutComponent::markLayoutNodeDirty() +{ + layoutNode().markDirtyAndPropagate(); + artboard()->markLayoutDirty(this); +} + +void LayoutComponent::markLayoutStyleDirty() +{ + clearInheritedInterpolation(); + addDirt(ComponentDirt::LayoutStyle); + if (artboard() != this) + { + artboard()->markLayoutStyleDirty(); + } +} +#else +Vec2D LayoutComponent::measureLayout(float width, + LayoutMeasureMode widthMode, + float height, + LayoutMeasureMode heightMode) +{ + return Vec2D(); +} + +void LayoutComponent::markLayoutNodeDirty() {} +void LayoutComponent::markLayoutStyleDirty() {} +void LayoutComponent::onDirty(ComponentDirt value) {} +bool LayoutComponent::mainAxisIsRow() { return true; } + +bool LayoutComponent::mainAxisIsColumn() { return false; } +#endif + +void LayoutComponent::clipChanged() { markLayoutNodeDirty(); } +void LayoutComponent::widthChanged() { markLayoutNodeDirty(); } +void LayoutComponent::heightChanged() { markLayoutNodeDirty(); } +void LayoutComponent::styleIdChanged() { markLayoutNodeDirty(); } diff --git a/src/math/aabb.cpp b/src/math/aabb.cpp index 23688fcc..c6fa11aa 100644 --- a/src/math/aabb.cpp +++ b/src/math/aabb.cpp @@ -80,3 +80,8 @@ void AABB::join(AABB& out, const AABB& a, const AABB& b) out.maxX = std::max(a.maxX, b.maxX); out.maxY = std::max(a.maxY, b.maxY); } + +bool AABB::contains(Vec2D point) const +{ + return point.x >= left() && point.x <= right() && point.y >= top() && point.y <= bottom(); +} diff --git a/src/math/bit_field_loc.cpp b/src/math/bit_field_loc.cpp new file mode 100644 index 00000000..377b10aa --- /dev/null +++ b/src/math/bit_field_loc.cpp @@ -0,0 +1,20 @@ +#include "rive/math/bit_field_loc.hpp" +#include + +using namespace rive; + +BitFieldLoc::BitFieldLoc(uint32_t start, uint32_t end) : m_start(start) +{ + assert(end >= start); + assert(end < 32); + + m_count = end - start + 1; + m_mask = ((1 << (end - start + 1)) - 1) << start; +} + +uint32_t BitFieldLoc::read(uint32_t bits) { return (bits & m_mask) >> m_start; } + +uint32_t BitFieldLoc::write(uint32_t bits, uint32_t value) +{ + return (bits & ~m_mask) | ((value << m_start) & m_mask); +} \ No newline at end of file diff --git a/src/math/contour_measure.cpp b/src/math/contour_measure.cpp index 0aa804af..89c1e425 100644 --- a/src/math/contour_measure.cpp +++ b/src/math/contour_measure.cpp @@ -34,7 +34,7 @@ ContourMeasure::ContourMeasure(std::vector&& segs, // or the last segment if distance == m_distance size_t ContourMeasure::findSegment(float distance) const { - assert(m_segments.front().m_distance > 0); + assert(m_segments.front().m_distance >= 0); assert(m_segments.back().m_distance == m_length); assert(distance >= 0 && distance <= m_length); @@ -66,6 +66,27 @@ static ContourMeasure::PosTan eval_quad(const Vec2D pts[], float t) static ContourMeasure::PosTan eval_cubic(const Vec2D pts[], float t) { assert(t >= 0 && t <= 1); + // When t==0 and t==1, the most accurate way to find tangents is by differencing. + if (t == 0 || t == 1) + { + if (t == 0) + { + return {pts[0], + (pts[0] != pts[1] ? pts[1] + : pts[1] != pts[2] ? pts[2] + : pts[3]) - + pts[0] + + }; + } + else + { + return {pts[3], + pts[3] - (pts[3] != pts[2] ? pts[2] + : pts[2] != pts[1] ? pts[1] + : pts[0])}; + } + } const EvalCubic eval(pts); @@ -139,11 +160,9 @@ void ContourMeasure::Segment::extract(RawPath* dst, ContourMeasure::PosTan ContourMeasure::getPosTan(float distance) const { // specal-case end of the contour - if (distance >= m_length) + if (distance > m_length) { - size_t N = m_points.size(); - assert(N > 1); - return {m_points[N - 1], (m_points[N - 1] - m_points[N - 2]).normalized()}; + distance = m_length; } if (distance < 0) @@ -228,9 +247,12 @@ static float compute_t(Span segs, size_t index, f } } - assert(prevDist < seg.m_distance); + assert(prevDist <= seg.m_distance); const auto ratio = (distance - prevDist) / (seg.m_distance - prevDist); - return lerp(prevT, seg.getT(), ratio); + float t = lerp(prevT, seg.getT(), ratio); + t = math::clamp(t, prevT, seg.getT()); + assert(prevT <= t && t <= seg.getT()); + return t; } void ContourMeasure::getSegment(float startDist, @@ -347,16 +369,16 @@ float ContourMeasureIter::addCubicSegs(ContourMeasure::Segment* segs, return distance; } -void ContourMeasureIter::rewind(const RawPath& path, float tolerance) +void ContourMeasureIter::rewind(const RawPath* path, float tolerance) { - m_iter = path.begin(); - m_end = path.end(); - m_srcPoints = path.points().data(); + m_iter = path->begin(); + m_end = path->end(); + m_srcPoints = path->points().data(); constexpr float kMinTolerance = 1.0f / 16; m_invTolerance = 1.0f / std::max(tolerance, kMinTolerance); - m_segmentCounts.resize(path.verbs().count()); + m_segmentCounts.resize(path->verbs().count()); } // Can return null if either it encountered an empty contour (length == 0) @@ -496,12 +518,15 @@ rcp ContourMeasureIter::tryNext() m_iter = endOfContour; - if (distance == 0 || pts.size() < 2) + if (distance > 0 && pts.size() >= 2) { - return nullptr; + assert(!std::isnan(distance)); + return rcp( + new ContourMeasure(std::move(segs), std::move(pts), distance, isClosed)); } - return rcp( - new ContourMeasure(std::move(segs), std::move(pts), distance, isClosed)); + + assert(distance == 0 || std::isnan(distance)); + return nullptr; } rcp ContourMeasureIter::next() @@ -518,5 +543,6 @@ rcp ContourMeasureIter::next() break; } } + assert(!cm || !std::isnan(cm->length())); return cm; } diff --git a/src/math/mat2d.cpp b/src/math/mat2d.cpp index 223e1b69..0980c8f4 100644 --- a/src/math/mat2d.cpp +++ b/src/math/mat2d.cpp @@ -90,11 +90,6 @@ void Mat2D::mapPoints(Vec2D dst[], const Vec2D pts[], size_t n) const AABB Mat2D::mapBoundingBox(const Vec2D pts[], size_t n) const { - if (n == 0) - { - return {0, 0, 0, 0}; - } - size_t i = 0; float4 scale = float2{m_buffer[0], m_buffer[3]}.xyxy; float4 skew = simd::load2f(&m_buffer[1]).yxyx; @@ -140,12 +135,23 @@ AABB Mat2D::mapBoundingBox(const Vec2D pts[], size_t n) const } float4 bbox = simd::join(simd::min(mins.xy, mins.zw), simd::max(maxes.xy, maxes.zw)); - assert(simd::all(bbox.xy <= bbox.zw)); - - float4 trans = simd::load2f(&m_buffer[4]).xyxy; - bbox += trans; + // Use logic that takes the "nonfinite" branch when bbox has NaN values. + // Use "b - a >= 0" instead of "a >= b" because it fails when b == a == inf. + if (!simd::all(bbox.zw - bbox.xy >= 0)) + { + // The given points were NaN or empty, or infinite. + bbox = float4(0); + } + else + { + float4 trans = simd::load2f(&m_buffer[4]).xyxy; + bbox += trans; + } - return math::bit_cast(bbox); + auto aabb = math::bit_cast(bbox); + assert(aabb.width() >= 0); + assert(aabb.height() >= 0); + return aabb; } AABB Mat2D::mapBoundingBox(const AABB& aabb) const diff --git a/src/nested_artboard.cpp b/src/nested_artboard.cpp index 8b80df48..6f30c4c1 100644 --- a/src/nested_artboard.cpp +++ b/src/nested_artboard.cpp @@ -6,6 +6,7 @@ #include "rive/nested_animation.hpp" #include "rive/animation/nested_state_machine.hpp" #include "rive/clip_result.hpp" +#include #include using namespace rive; @@ -38,12 +39,15 @@ void NestedArtboard::nest(Artboard* artboard) } m_Artboard->frameOrigin(false); m_Artboard->opacity(renderOpacity()); + m_Artboard->volume(artboard->volume()); m_Instance = nullptr; if (artboard->isInstance()) { m_Instance.reset(static_cast(artboard)); // take ownership } - m_Artboard->advance(0.0f); + // This allows for swapping after initial load (after onAddedClean has + // already been called). + m_Artboard->host(this); } static Mat2D makeTranslate(const Artboard* artboard) @@ -58,7 +62,7 @@ void NestedArtboard::draw(Renderer* renderer) { return; } - ClipResult clipResult = clip(renderer); + ClipResult clipResult = applyClip(renderer); if (clipResult == ClipResult::noClip) { // We didn't clip, so make sure to save as we'll be doing some @@ -81,7 +85,7 @@ Core* NestedArtboard::hitTest(HitInfo* hinfo, const Mat2D& xform) } hinfo->mounts.push_back(this); auto mx = xform * worldTransform() * makeTranslate(m_Artboard); - if (auto c = m_Artboard->hitTest(hinfo, &mx)) + if (auto c = m_Artboard->hitTest(hinfo, mx)) { return c; } @@ -122,21 +126,23 @@ StatusCode NestedArtboard::onAddedClean(CoreContext* context) { animation->initializeAnimation(m_Instance.get()); } + m_Artboard->host(this); } return Super::onAddedClean(context); } bool NestedArtboard::advance(float elapsedSeconds) { + bool keepGoing = false; if (m_Artboard == nullptr || isCollapsed()) { - return false; + return keepGoing; } for (auto animation : m_NestedAnimations) { - animation->advance(elapsedSeconds); + keepGoing = animation->advance(elapsedSeconds) || keepGoing; } - return m_Artboard->advance(elapsedSeconds); + return m_Artboard->advanceInternal(elapsedSeconds, false) || keepGoing; } void NestedArtboard::update(ComponentDirt value) @@ -162,6 +168,56 @@ bool NestedArtboard::hasNestedStateMachines() const Span NestedArtboard::nestedAnimations() { return m_NestedAnimations; } +NestedArtboard* NestedArtboard::nestedArtboard(std::string name) const +{ + if (m_Instance != nullptr) + { + return m_Instance->nestedArtboard(name); + } + return nullptr; +} + +NestedStateMachine* NestedArtboard::stateMachine(std::string name) const +{ + for (auto animation : m_NestedAnimations) + { + if (animation->is() && animation->name() == name) + { + return animation->as(); + } + } + return nullptr; +} + +NestedInput* NestedArtboard::input(std::string name) const { return input(name, ""); } + +NestedInput* NestedArtboard::input(std::string name, std::string stateMachineName) const +{ + if (!stateMachineName.empty()) + { + auto nestedSM = stateMachine(stateMachineName); + if (nestedSM != nullptr) + { + return nestedSM->input(name); + } + } + else + { + for (auto animation : m_NestedAnimations) + { + if (animation->is()) + { + auto input = animation->as()->input(name); + if (input != nullptr) + { + return input; + } + } + } + } + return nullptr; +} + bool NestedArtboard::worldToLocal(Vec2D world, Vec2D* local) { assert(local != nullptr); @@ -178,4 +234,69 @@ bool NestedArtboard::worldToLocal(Vec2D world, Vec2D* local) *local = toMountedArtboard * world; return true; +} + +Vec2D NestedArtboard::measureLayout(float width, + LayoutMeasureMode widthMode, + float height, + LayoutMeasureMode heightMode) +{ + return Vec2D( + std::min(widthMode == LayoutMeasureMode::undefined ? std::numeric_limits::max() + : width, + m_Instance ? m_Instance->width() : 0.0f), + std::min(heightMode == LayoutMeasureMode::undefined ? std::numeric_limits::max() + : height, + m_Instance ? m_Instance->height() : 0.0f)); +} + +void NestedArtboard::syncStyleChanges() +{ + if (m_Artboard == nullptr) + { + return; + } + m_Artboard->syncStyleChanges(); +} + +void NestedArtboard::controlSize(Vec2D size) {} + +void NestedArtboard::decodeDataBindPathIds(Span value) +{ + BinaryReader reader(value); + while (!reader.reachedEnd()) + { + auto val = reader.readVarUintAs(); + m_DataBindPathIdsBuffer.push_back(val); + } +} + +void NestedArtboard::copyDataBindPathIds(const NestedArtboardBase& object) +{ + m_DataBindPathIdsBuffer = object.as()->m_DataBindPathIdsBuffer; +} + +void NestedArtboard::internalDataContext(DataContext* value, DataContext* parent) +{ + artboardInstance()->internalDataContext(value, parent, false); + for (auto animation : m_NestedAnimations) + { + if (animation->is()) + { + animation->as()->dataContext(value); + } + } +} + +void NestedArtboard::dataContextFromInstance(ViewModelInstance* viewModelInstance, + DataContext* parent) +{ + artboardInstance()->dataContextFromInstance(viewModelInstance, parent, false); + for (auto animation : m_NestedAnimations) + { + if (animation->is()) + { + animation->as()->dataContextFromInstance(viewModelInstance); + } + } } \ No newline at end of file diff --git a/src/nested_artboard_layout.cpp b/src/nested_artboard_layout.cpp new file mode 100644 index 00000000..501ffd67 --- /dev/null +++ b/src/nested_artboard_layout.cpp @@ -0,0 +1,138 @@ +#include "rive/nested_artboard_layout.hpp" +#include "rive/artboard.hpp" + +using namespace rive; + +Core* NestedArtboardLayout::clone() const +{ + NestedArtboardLayout* nestedArtboard = + static_cast(NestedArtboardLayoutBase::clone()); + if (m_Artboard == nullptr) + { + return nestedArtboard; + } + auto ni = m_Artboard->instance(); + nestedArtboard->nest(ni.release()); + return nestedArtboard; +} + +float NestedArtboardLayout::actualInstanceWidth() +{ + return instanceWidth() == -1.0f ? artboardInstance()->originalWidth() : instanceWidth(); +} + +float NestedArtboardLayout::actualInstanceHeight() +{ + return instanceHeight() == -1.0f ? artboardInstance()->originalHeight() : instanceHeight(); +} + +#ifdef WITH_RIVE_LAYOUT +void* NestedArtboardLayout::layoutNode() +{ + if (artboardInstance() == nullptr) + { + return nullptr; + } + return artboardInstance()->takeLayoutNode(); +} +#endif + +void NestedArtboardLayout::markNestedLayoutDirty() +{ + if (artboardInstance() != nullptr) + { + artboardInstance()->markLayoutNodeDirty(); + } +} + +void NestedArtboardLayout::update(ComponentDirt value) +{ + Super::update(value); + auto artboard = artboardInstance(); + if (hasDirt(value, ComponentDirt::WorldTransform) && artboard != nullptr) + { + auto layoutPosition = Vec2D(artboard->layoutX(), artboard->layoutY()); + + if (parent()->is()) + { + auto parentArtboard = parent()->as(); + auto correctedArtboardSpace = + Mat2D::fromTranslation(parentArtboard->origin() + layoutPosition); + m_WorldTransform = correctedArtboardSpace * m_WorldTransform; + } + else + { + m_WorldTransform = Mat2D::fromTranslation(layoutPosition) * m_WorldTransform; + } + auto back = Mat2D::fromTranslation(-artboard->origin()); + m_WorldTransform = back * m_WorldTransform; + } +} + +StatusCode NestedArtboardLayout::onAddedClean(CoreContext* context) +{ + StatusCode code = Super::onAddedClean(context); + if (code != StatusCode::Ok) + { + return code; + } + + updateWidthOverride(); + updateHeightOverride(); + + return StatusCode::Ok; +} + +void NestedArtboardLayout::updateWidthOverride() +{ + if (artboardInstance() == nullptr) + { + return; + } + auto isRow = + parent()->is() ? parent()->as()->mainAxisIsRow() : true; + if (instanceWidthScaleType() == 0) // LayoutScaleType::fixed + { + // If we're set to fixed, pass the unit value (points|percent) + artboardInstance()->widthOverride(actualInstanceWidth(), instanceWidthUnitsValue(), isRow); + } + else if (instanceWidthScaleType() == 1) // LayoutScaleType::fill + { + // If we're set to fill, pass auto + artboardInstance()->widthOverride(actualInstanceWidth(), 3, isRow); + } +} + +void NestedArtboardLayout::updateHeightOverride() +{ + if (artboardInstance() == nullptr) + { + return; + } + auto isRow = + parent()->is() ? parent()->as()->mainAxisIsRow() : true; + if (instanceHeightScaleType() == 0) // LayoutScaleType::fixed + { + // If we're set to fixed, pass the unit value (points|percent) + artboardInstance()->heightOverride(actualInstanceHeight(), + instanceHeightUnitsValue(), + isRow); + } + else if (instanceHeightScaleType() == 1) // LayoutScaleType::fill + { + // If we're set to fill, pass auto + artboardInstance()->heightOverride(actualInstanceHeight(), 3, isRow); + } +} + +void NestedArtboardLayout::instanceWidthChanged() { updateWidthOverride(); } + +void NestedArtboardLayout::instanceHeightChanged() { updateHeightOverride(); } + +void NestedArtboardLayout::instanceWidthUnitsValueChanged() { updateWidthOverride(); } + +void NestedArtboardLayout::instanceHeightUnitsValueChanged() { updateHeightOverride(); } + +void NestedArtboardLayout::instanceWidthScaleTypeChanged() { updateWidthOverride(); } + +void NestedArtboardLayout::instanceHeightScaleTypeChanged() { updateHeightOverride(); } \ No newline at end of file diff --git a/src/nested_artboard_leaf.cpp b/src/nested_artboard_leaf.cpp new file mode 100644 index 00000000..2ae92b6e --- /dev/null +++ b/src/nested_artboard_leaf.cpp @@ -0,0 +1,40 @@ +#include "rive/nested_artboard_leaf.hpp" +#include "rive/renderer.hpp" +#include "rive/layout_component.hpp" +#include "rive/artboard.hpp" + +using namespace rive; + +Core* NestedArtboardLeaf::clone() const +{ + NestedArtboardLeaf* nestedArtboard = + static_cast(NestedArtboardLeafBase::clone()); + if (m_Artboard == nullptr) + { + return nestedArtboard; + } + auto ni = m_Artboard->instance(); + nestedArtboard->nest(ni.release()); + return nestedArtboard; +} + +void NestedArtboardLeaf::update(ComponentDirt value) +{ + Super::update(value); + auto artboard = artboardInstance(); + if (hasDirt(value, ComponentDirt::WorldTransform) && artboard != nullptr) + { + auto p = parent(); + + AABB bounds = p != nullptr && p->is() + ? p->as()->localBounds() + : AABB(); + + auto viewTransform = computeAlignment((Fit)fit(), + Alignment(alignmentX(), alignmentY()), + bounds, + artboard->bounds()); + + m_WorldTransform *= viewTransform; + } +} diff --git a/src/scene.cpp b/src/scene.cpp index a987dfec..679f33e2 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -25,6 +25,7 @@ SMIInput* Scene::input(size_t index) const { return nullptr; } SMIBool* Scene::getBool(const std::string&) const { return nullptr; } SMINumber* Scene::getNumber(const std::string&) const { return nullptr; } SMITrigger* Scene::getTrigger(const std::string&) const { return nullptr; } +void Scene::dataContextFromInstance(ViewModelInstance* viewModelInstance) {} void Scene::reportKeyedCallback(uint32_t objectId, uint32_t propertyKey, float elapsedSeconds) { diff --git a/src/shapes/clipping_shape.cpp b/src/shapes/clipping_shape.cpp index cfa7f902..e795a6ce 100644 --- a/src/shapes/clipping_shape.cpp +++ b/src/shapes/clipping_shape.cpp @@ -47,7 +47,7 @@ StatusCode ClippingShape::onAddedClean(CoreContext* context) if (component == m_Source) { auto shape = core->as(); - shape->addDefaultPathSpace(PathSpace::World | PathSpace::Clipping); + shape->addFlags(PathFlags::world | PathFlags::clipping); m_Shapes.push_back(shape); break; } diff --git a/src/shapes/image.cpp b/src/shapes/image.cpp index 021f84ba..d86687e3 100644 --- a/src/shapes/image.cpp +++ b/src/shapes/image.cpp @@ -24,7 +24,7 @@ void Image::draw(Renderer* renderer) return; } - ClipResult clipResult = clip(renderer); + ClipResult clipResult = applyClip(renderer); if (clipResult == ClipResult::noClip) { @@ -121,3 +121,81 @@ Core* Image::clone() const void Image::setMesh(Mesh* mesh) { m_Mesh = mesh; } Mesh* Image::mesh() const { return m_Mesh; } + +float Image::width() const +{ + rive::ImageAsset* asset = this->imageAsset(); + if (asset == nullptr) + { + return 0.0f; + } + + rive::RenderImage* renderImage = asset->renderImage(); + if (renderImage == nullptr) + { + return 0.0f; + } + return renderImage->width(); +} + +float Image::height() const +{ + rive::ImageAsset* asset = this->imageAsset(); + if (asset == nullptr) + { + return 0.0f; + } + + rive::RenderImage* renderImage = asset->renderImage(); + if (renderImage == nullptr) + { + return 0.0f; + } + return renderImage->height(); +} + +Vec2D Image::measureLayout(float width, + LayoutMeasureMode widthMode, + float height, + LayoutMeasureMode heightMode) +{ + float measuredWidth, measuredHeight; + switch (widthMode) + { + case LayoutMeasureMode::atMost: + measuredWidth = std::max(Image::width(), width); + break; + case LayoutMeasureMode::exactly: + measuredWidth = width; + break; + case LayoutMeasureMode::undefined: + measuredWidth = Image::width(); + break; + } + switch (heightMode) + { + case LayoutMeasureMode::atMost: + measuredHeight = std::max(Image::height(), height); + break; + case LayoutMeasureMode::exactly: + measuredHeight = height; + break; + case LayoutMeasureMode::undefined: + measuredHeight = Image::height(); + break; + } + return Vec2D(measuredWidth, measuredHeight); +} + +void Image::controlSize(Vec2D size) +{ + auto renderImage = imageAsset()->renderImage(); + auto newScaleX = size.x / renderImage->width(); + auto newScaleY = size.y / renderImage->height(); + if (newScaleX != scaleX() || newScaleY != scaleY()) + { + scaleX(newScaleX); + scaleY(newScaleY); + addDirt(ComponentDirt::WorldTransform, false); + } +} \ No newline at end of file diff --git a/src/shapes/metrics_path.cpp b/src/shapes/metrics_path.cpp deleted file mode 100644 index 46fea411..00000000 --- a/src/shapes/metrics_path.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include "rive/shapes/metrics_path.hpp" -#include "rive/renderer.hpp" -#include "rive/math/raw_path.hpp" -#include "rive/math/contour_measure.hpp" -#include - -using namespace rive; - -void MetricsPath::rewind() -{ - for (auto ptr : m_Paths) - { - delete ptr; - } - m_Paths.clear(); - m_Contour.reset(nullptr); - m_RawPath.rewind(); - m_ComputedLengthTransform = Mat2D(); - m_ComputedLength = 0; -} - -MetricsPath::~MetricsPath() { rewind(); } - -void MetricsPath::addPath(CommandPath* path, const Mat2D& transform) -{ - MetricsPath* metricsPath = static_cast(path); - m_ComputedLength += metricsPath->computeLength(transform); - // We need to copy the data to avoid contention between multiple uses of the same path - // for example when the same path is added as localPath and worldPath - auto metricsPathCopy = new OnlyMetricsPath(); - metricsPathCopy->m_Contour = metricsPath->m_Contour; - metricsPathCopy->m_RawPath = metricsPath->m_RawPath; - metricsPathCopy->m_ComputedLength = metricsPath->m_ComputedLength; - m_Paths.emplace_back(metricsPathCopy); -} - -RawPath::Iter MetricsPath::addToRawPath(RawPath& rawPath, const Mat2D& transform) const -{ - return rawPath.addPath(m_RawPath, &transform); -} - -void MetricsPath::moveTo(float x, float y) -{ - assert(m_RawPath.points().size() == 0); - m_RawPath.move({x, y}); -} - -void MetricsPath::lineTo(float x, float y) { m_RawPath.line({x, y}); } - -void MetricsPath::cubicTo(float ox, float oy, float ix, float iy, float x, float y) -{ - m_RawPath.cubic({ox, oy}, {ix, iy}, {x, y}); -} - -void MetricsPath::close() -{ - // Should we pass the close() to our m_RawPath ??? -} - -float MetricsPath::computeLength(const Mat2D& transform) -{ - // Only compute if our pre-computed length is not valid - if (!m_Contour || transform != m_ComputedLengthTransform) - { - m_ComputedLengthTransform = transform; - m_Contour = ContourMeasureIter(m_RawPath * transform).next(); - m_ComputedLength = m_Contour ? m_Contour->length() : 0; - } - return m_ComputedLength; -} - -void MetricsPath::trim(float startLength, float endLength, bool moveTo, RawPath* result) -{ - assert(endLength >= startLength); - if (!m_Paths.empty()) - { - m_Paths.front()->trim(startLength, endLength, moveTo, result); - return; - } - if (!m_Contour) - { - // All the contours were 0 length, so there's nothing to segment. - return; - } - // TODO: if we can change the signature of MetricsPath and/or trim() to speak native - // rawpaths, we wouldn't need this temporary copy (since ContourMeasure speaks - // native rawpaths). - RawPath tmp; - m_Contour->getSegment(startLength, endLength, result, moveTo); -} - -RenderMetricsPath::RenderMetricsPath(rcp path) : m_RenderPath(std::move(path)) {} - -void RenderMetricsPath::addPath(CommandPath* path, const Mat2D& transform) -{ - MetricsPath::addPath(path, transform); - m_RenderPath->addPath(path->renderPath(), transform); -} - -void RenderMetricsPath::rewind() -{ - MetricsPath::rewind(); - m_RenderPath->rewind(); -} - -void RenderMetricsPath::moveTo(float x, float y) -{ - MetricsPath::moveTo(x, y); - m_RenderPath->moveTo(x, y); -} - -void RenderMetricsPath::lineTo(float x, float y) -{ - MetricsPath::lineTo(x, y); - m_RenderPath->lineTo(x, y); -} - -void RenderMetricsPath::cubicTo(float ox, float oy, float ix, float iy, float x, float y) -{ - MetricsPath::cubicTo(ox, oy, ix, iy, x, y); - m_RenderPath->cubicTo(ox, oy, ix, iy, x, y); -} - -void RenderMetricsPath::close() -{ - MetricsPath::close(); - m_RenderPath->close(); -} - -void RenderMetricsPath::fillRule(FillRule value) { m_RenderPath->fillRule(value); } diff --git a/src/shapes/paint/fill.cpp b/src/shapes/paint/fill.cpp index 60c39d88..d0af7ef1 100644 --- a/src/shapes/paint/fill.cpp +++ b/src/shapes/paint/fill.cpp @@ -2,7 +2,7 @@ using namespace rive; -PathSpace Fill::pathSpace() const { return PathSpace::Local; } +PathFlags Fill::pathFlags() const { return PathFlags::local; } RenderPaint* Fill::initRenderPaint(ShapePaintMutator* mutator) { @@ -18,7 +18,7 @@ void Fill::applyTo(RenderPaint* renderPaint, float opacityModifier) const m_PaintMutator->applyTo(renderPaint, opacityModifier); } -void Fill::draw(Renderer* renderer, CommandPath* path, RenderPaint* paint) +void Fill::draw(Renderer* renderer, CommandPath* path, const RawPath* rawPath, RenderPaint* paint) { if (!isVisible()) { diff --git a/src/shapes/paint/linear_gradient.cpp b/src/shapes/paint/linear_gradient.cpp index aa49c945..0bcc1c6a 100644 --- a/src/shapes/paint/linear_gradient.cpp +++ b/src/shapes/paint/linear_gradient.cpp @@ -84,7 +84,7 @@ void LinearGradient::update(ComponentDirt value) ComponentDirt::Paint | ComponentDirt::RenderOpacity | ComponentDirt::Transform) || ( // paints in world space - parent()->as()->pathSpace() == PathSpace::World && + parent()->as()->isFlagged(PathFlags::world) && // and had a world transform change hasDirt(value, ComponentDirt::WorldTransform)); if (rebuildGradient) @@ -95,7 +95,7 @@ void LinearGradient::update(ComponentDirt value) void LinearGradient::applyTo(RenderPaint* renderPaint, float opacityModifier) const { - bool paintsInWorldSpace = parent()->as()->pathSpace() == PathSpace::World; + bool paintsInWorldSpace = parent()->as()->isFlagged(PathFlags::world); Vec2D start(startX(), startY()); Vec2D end(endX(), endY()); // Check if we need to update the world space gradient (if there's no diff --git a/src/shapes/paint/stroke.cpp b/src/shapes/paint/stroke.cpp index a32b7b38..a76adaee 100644 --- a/src/shapes/paint/stroke.cpp +++ b/src/shapes/paint/stroke.cpp @@ -6,11 +6,10 @@ using namespace rive; -PathSpace Stroke::pathSpace() const +PathFlags Stroke::pathFlags() const { - return transformAffectsStroke() ? PathSpace::Local : PathSpace::World; + return transformAffectsStroke() ? PathFlags::local : PathFlags::world; } - RenderPaint* Stroke::initRenderPaint(ShapePaintMutator* mutator) { auto renderPaint = Super::initRenderPaint(mutator); @@ -33,18 +32,17 @@ void Stroke::applyTo(RenderPaint* renderPaint, float opacityModifier) const bool Stroke::isVisible() const { return Super::isVisible() && thickness() > 0.0f; } -void Stroke::draw(Renderer* renderer, CommandPath* path, RenderPaint* paint) +void Stroke::draw(Renderer* renderer, CommandPath* path, const RawPath* rawPath, RenderPaint* paint) { if (!isVisible()) { return; } - if (m_Effect != nullptr) + if (m_Effect != nullptr && rawPath != nullptr) { - /// We're guaranteed to get a metrics path here if we have an effect. auto factory = artboard()->factory(); - path = m_Effect->effectPath(reinterpret_cast(path), factory); + path = m_Effect->effectPath(*rawPath, factory); } renderer->drawPath(path->renderPath(), paint); diff --git a/src/shapes/paint/trim_path.cpp b/src/shapes/paint/trim_path.cpp index 7fad5424..8cdabd73 100644 --- a/src/shapes/paint/trim_path.cpp +++ b/src/shapes/paint/trim_path.cpp @@ -1,5 +1,4 @@ #include "rive/shapes/paint/trim_path.hpp" -#include "rive/shapes/metrics_path.hpp" #include "rive/shapes/paint/stroke.hpp" #include "rive/factory.hpp" @@ -17,33 +16,29 @@ StatusCode TrimPath::onAddedClean(CoreContext* context) return StatusCode::Ok; } -RenderPath* TrimPath::effectPath(MetricsPath* source, Factory* factory) +void TrimPath::trimRawPath(const RawPath& source) { - if (m_RenderPath != nullptr) - { - return m_RenderPath; - } - - // Source is always a containing (shape) path. - const std::vector& subPaths = source->paths(); - - RawPath rawTrimmed; + m_rawPath.rewind(); + auto renderOffset = std::fmod(std::fmod(offset(), 1.0f) + 1.0f, 1.0f); - if (!m_TrimmedPath) + // Build up contours if they're empty. + if (m_contours.empty()) { - m_TrimmedPath = factory->makeEmptyRenderPath(); + ContourMeasureIter iter(&source); + while (auto meas = iter.next()) + { + m_contours.push_back(meas); + } } - else + switch (mode()) { - m_TrimmedPath->rewind(); - } - - auto renderOffset = std::fmod(std::fmod(offset(), 1.0f) + 1.0f, 1.0f); - switch (modeValue()) - { - case 1: + case TrimPathMode::sequential: { - float totalLength = source->length(); + float totalLength = 0.0f; + for (auto contour : m_contours) + { + totalLength += contour->length(); + } auto startLength = totalLength * (start() + renderOffset); auto endLength = totalLength * (end() + renderOffset); @@ -60,35 +55,35 @@ RenderPath* TrimPath::effectPath(MetricsPath* source, Factory* factory) endLength -= totalLength; } - int i = 0, subPathCount = (int)subPaths.size(); + int i = 0, subPathCount = (int)m_contours.size(); while (endLength > 0) { - auto path = subPaths[i % subPathCount]; - auto pathLength = path->length(); + auto contour = m_contours[i % subPathCount]; + auto contourLength = contour->length(); - if (startLength < pathLength) + if (startLength < contourLength) { - path->trim(startLength, endLength, true, &rawTrimmed); - endLength -= pathLength; + contour->getSegment(startLength, endLength, &m_rawPath, true); + endLength -= contourLength; startLength = 0; } else { - startLength -= pathLength; - endLength -= pathLength; + startLength -= contourLength; + endLength -= contourLength; } i++; } } break; - case 2: + case TrimPathMode::synchronized: { - for (auto path : subPaths) + for (auto contour : m_contours) { - auto pathLength = path->length(); - auto startLength = pathLength * (start() + renderOffset); - auto endLength = pathLength * (end() + renderOffset); + auto contourLength = contour->length(); + auto startLength = contourLength * (start() + renderOffset); + auto endLength = contourLength * (end() + renderOffset); if (endLength < startLength) { auto length = startLength; @@ -96,37 +91,86 @@ RenderPath* TrimPath::effectPath(MetricsPath* source, Factory* factory) endLength = length; } - if (startLength > pathLength) + if (startLength > contourLength) { - startLength -= pathLength; - endLength -= pathLength; + startLength -= contourLength; + endLength -= contourLength; } - path->trim(startLength, endLength, true, &rawTrimmed); - while (endLength > pathLength) + contour->getSegment(startLength, endLength, &m_rawPath, true); + while (endLength > contourLength) { startLength = 0; - endLength -= pathLength; - path->trim(startLength, endLength, true, &rawTrimmed); + endLength -= contourLength; + contour->getSegment(startLength, endLength, &m_rawPath, true); } } } break; + default: + RIVE_UNREACHABLE(); + } +} + +RenderPath* TrimPath::effectPath(const RawPath& source, Factory* factory) +{ + if (m_renderPath != nullptr) + { + // Previous result hasn't been invalidated, it's still good. + return m_renderPath; } - m_RenderPath = m_TrimmedPath.get(); - rawTrimmed.addTo(m_RenderPath); - return m_RenderPath; + trimRawPath(source); + + if (!m_trimmedPath) + { + m_trimmedPath = factory->makeEmptyRenderPath(); + } + else + { + m_trimmedPath->rewind(); + } + + m_renderPath = m_trimmedPath.get(); + m_rawPath.addTo(m_renderPath); + return m_renderPath; } void TrimPath::invalidateEffect() { - m_RenderPath = nullptr; - auto stroke = parent()->as(); - stroke->parent()->addDirt(ComponentDirt::Paint); - stroke->invalidateRendering(); + invalidateTrim(); + // This is usually sent when the path is changed so we need to also + // invalidate the contours, not just the trim effect. + m_contours.clear(); +} + +void TrimPath::invalidateTrim() +{ + m_renderPath = nullptr; + if (parent() != nullptr) + { + auto stroke = parent()->as(); + stroke->parent()->addDirt(ComponentDirt::Paint); + stroke->invalidateRendering(); + } } -void TrimPath::startChanged() { invalidateEffect(); } -void TrimPath::endChanged() { invalidateEffect(); } -void TrimPath::offsetChanged() { invalidateEffect(); } -void TrimPath::modeValueChanged() { invalidateEffect(); } +void TrimPath::startChanged() { invalidateTrim(); } +void TrimPath::endChanged() { invalidateTrim(); } +void TrimPath::offsetChanged() { invalidateTrim(); } +void TrimPath::modeValueChanged() { invalidateTrim(); } + +StatusCode TrimPath::onAddedDirty(CoreContext* context) +{ + auto code = Super::onAddedDirty(context); + if (code != StatusCode::Ok) + { + return code; + } + switch (mode()) + { + case TrimPathMode::sequential: + case TrimPathMode::synchronized: + return StatusCode::Ok; + } + return StatusCode::InvalidObject; +} \ No newline at end of file diff --git a/src/shapes/parametric_path.cpp b/src/shapes/parametric_path.cpp index 923d1d7d..aa39cf32 100644 --- a/src/shapes/parametric_path.cpp +++ b/src/shapes/parametric_path.cpp @@ -1,7 +1,61 @@ +#include "rive/layout_component.hpp" +#include "rive/math/aabb.hpp" +#include "rive/node.hpp" #include "rive/shapes/parametric_path.hpp" +#include "rive/shapes/shape.hpp" using namespace rive; +Vec2D ParametricPath::measureLayout(float width, + LayoutMeasureMode widthMode, + float height, + LayoutMeasureMode heightMode) +{ + return Vec2D( + std::min( + (widthMode == LayoutMeasureMode::undefined ? std::numeric_limits::max() : width), + ParametricPath::width()), + std::min((heightMode == LayoutMeasureMode::undefined ? std::numeric_limits::max() + : height), + ParametricPath::height())); +} + +void ParametricPath::controlSize(Vec2D size) +{ + width(size.x); + height(size.y); + markWorldTransformDirty(); + markPathDirty(false); +} + +void ParametricPath::markPathDirty(bool sendToLayout) +{ + Super::markPathDirty(); +#ifdef WITH_RIVE_LAYOUT + if (sendToLayout) + { + for (ContainerComponent* p = parent(); p != nullptr; p = p->parent()) + { + if (p->is()) + { + p->as()->markLayoutNodeDirty(); + break; + } + // If we're in a group we break out because objects in groups do + // not affect nor are affected by parent LayoutComponents + if (p->is()) + { + if (p->is() && p->as() == shape()) + { + continue; + } + break; + } + } + } +#endif +} + void ParametricPath::widthChanged() { markPathDirty(); } void ParametricPath::heightChanged() { markPathDirty(); } void ParametricPath::originXChanged() { markPathDirty(); } diff --git a/src/shapes/path.cpp b/src/shapes/path.cpp index 71371f3e..44784db5 100644 --- a/src/shapes/path.cpp +++ b/src/shapes/path.cpp @@ -47,22 +47,32 @@ StatusCode Path::onAddedClean(CoreContext* context) return StatusCode::MissingObject; } -void Path::buildDependencies() -{ - Super::buildDependencies(); - // Make sure this is called once the shape has all of the paints added - // (paints get added during the added cycle so buildDependencies is a good - // time to do this.) - m_CommandPath = m_Shape->makeCommandPath(m_DefaultPathSpace); -} +void Path::buildDependencies() { Super::buildDependencies(); } void Path::addVertex(PathVertex* vertex) { m_Vertices.push_back(vertex); } -void Path::addDefaultPathSpace(PathSpace space) { m_DefaultPathSpace |= space; } +void Path::addFlags(PathFlags flags) { m_pathFlags |= flags; } +bool Path::isFlagged(PathFlags flags) const { return (int)(m_pathFlags & flags) != 0x00; } + +bool Path::canDeferPathUpdate() +{ + if (m_Shape == nullptr) + { + return false; + } + // A path cannot defer its update if the shapes requires an update. Note the + // nuance here where we track that the shape may be marked for follow path + // (meaning all child paths need to follow path). This doesn't mean the + // Shape is necessarily forced to update put the paths are, which is why we + // explicitly also check the shape's path space. + + return m_Shape->canDeferPathUpdate() && !m_Shape->isFlagged(PathFlags::followPath) && + !isFlagged(PathFlags::followPath | PathFlags::clipping); +} const Mat2D& Path::pathTransform() const { return worldTransform(); } -void Path::buildPath(CommandPath& commandPath) const +void Path::buildPath(RawPath& rawPath) const { const bool isClosed = isPathClosed(); const std::vector& vertices = m_Vertices; @@ -88,7 +98,7 @@ void Path::buildPath(CommandPath& commandPath) const startIn = cubic->renderIn(); out = cubic->renderOut(); start = cubic->renderTranslation(); - commandPath.move(start); + rawPath.move(start); } else { @@ -119,18 +129,18 @@ void Path::buildPath(CommandPath& commandPath) const float idealDistance = computeIdealControlPointDistance(toPrev, toNext, renderRadius); startIn = start = Vec2D::scaleAndAdd(pos, toPrev, renderRadius); - commandPath.move(startIn); + rawPath.move(startIn); Vec2D outPoint = Vec2D::scaleAndAdd(pos, toPrev, renderRadius - idealDistance); Vec2D inPoint = Vec2D::scaleAndAdd(pos, toNext, renderRadius - idealDistance); out = Vec2D::scaleAndAdd(pos, toNext, renderRadius); - commandPath.cubic(outPoint, inPoint, out); + rawPath.cubic(outPoint, inPoint, out); prevIsCubic = false; } else { startIn = start = out = point.renderTranslation(); - commandPath.move(out); + rawPath.move(out); } } @@ -144,7 +154,7 @@ void Path::buildPath(CommandPath& commandPath) const auto inPoint = cubic->renderIn(); auto translation = cubic->renderTranslation(); - commandPath.cubic(out, inPoint, translation); + rawPath.cubic(out, inPoint, translation); prevIsCubic = true; out = cubic->renderOut(); @@ -177,22 +187,22 @@ void Path::buildPath(CommandPath& commandPath) const Vec2D translation = Vec2D::scaleAndAdd(pos, toPrev, renderRadius); if (prevIsCubic) { - commandPath.cubic(out, translation, translation); + rawPath.cubic(out, translation, translation); } else { - commandPath.line(translation); + rawPath.line(translation); } Vec2D outPoint = Vec2D::scaleAndAdd(pos, toPrev, renderRadius - idealDistance); Vec2D inPoint = Vec2D::scaleAndAdd(pos, toNext, renderRadius - idealDistance); out = Vec2D::scaleAndAdd(pos, toNext, renderRadius); - commandPath.cubic(outPoint, inPoint, out); + rawPath.cubic(outPoint, inPoint, out); prevIsCubic = false; } else if (prevIsCubic) { - commandPath.cubic(out, pos, pos); + rawPath.cubic(out, pos, pos); prevIsCubic = false; out = pos; @@ -200,7 +210,7 @@ void Path::buildPath(CommandPath& commandPath) const else { out = pos; - commandPath.line(out); + rawPath.line(out); } } } @@ -208,17 +218,17 @@ void Path::buildPath(CommandPath& commandPath) const { if (prevIsCubic || startIsCubic) { - commandPath.cubic(out, startIn, start); + rawPath.cubic(out, startIn, start); } else { - commandPath.line(start); + rawPath.line(start); } - commandPath.close(); + rawPath.close(); } } -void Path::markPathDirty() +void Path::markPathDirty(bool sendToLayout) { addDirt(ComponentDirt::Path); if (m_Shape != nullptr) @@ -243,9 +253,9 @@ void Path::update(ComponentDirt value) { Super::update(value); - if (m_CommandPath != nullptr && hasDirt(value, ComponentDirt::Path)) + if (hasDirt(value, ComponentDirt::Path)) { - if (m_Shape->canDeferPathUpdate()) + if (canDeferPathUpdate()) { m_deferredPathDirt = true; return; @@ -254,8 +264,8 @@ void Path::update(ComponentDirt value) // Build path doesn't explicitly rewind because we use it to concatenate // multiple built paths into a single command path (like the hit // tester). - m_CommandPath->rewind(); - buildPath(*m_CommandPath); + m_rawPath.rewind(); + buildPath(m_rawPath); } // if (hasDirt(value, ComponentDirt::WorldTransform) && m_Shape != nullptr) // { diff --git a/src/shapes/path_composer.cpp b/src/shapes/path_composer.cpp index adaef9cf..5efda247 100644 --- a/src/shapes/path_composer.cpp +++ b/src/shapes/path_composer.cpp @@ -3,16 +3,17 @@ #include "rive/renderer.hpp" #include "rive/shapes/path.hpp" #include "rive/shapes/shape.hpp" +#include "rive/factory.hpp" using namespace rive; -PathComposer::PathComposer(Shape* shape) : m_Shape(shape), m_deferredPathDirt(false) {} +PathComposer::PathComposer(Shape* shape) : m_shape(shape), m_deferredPathDirt(false) {} void PathComposer::buildDependencies() { - assert(m_Shape != nullptr); - m_Shape->addDependent(this); - for (auto path : m_Shape->paths()) + assert(m_shape != nullptr); + m_shape->addDependent(this); + for (auto path : m_shape->paths()) { path->addDependent(this); } @@ -25,7 +26,7 @@ void PathComposer::onDirty(ComponentDirt dirt) // We'd deferred the update, let's make sure the rest of our // dependencies update too. Constraints need to update too, stroke // effects, etc. - m_Shape->pathChanged(); + m_shape->pathChanged(); } } @@ -33,60 +34,62 @@ void PathComposer::update(ComponentDirt value) { if (hasDirt(value, ComponentDirt::Path)) { - if (m_Shape->canDeferPathUpdate()) + if (m_shape->canDeferPathUpdate()) { m_deferredPathDirt = true; return; } m_deferredPathDirt = false; - auto space = m_Shape->pathSpace(); - bool hasConstraint = (space & PathSpace::FollowPath) == PathSpace::FollowPath; - if ((space & PathSpace::Local) == PathSpace::Local) + if (m_shape->isFlagged(PathFlags::local)) { - if (m_LocalPath == nullptr) + if (m_localPath == nullptr) { - PathSpace localSpace = - (hasConstraint) ? PathSpace::Local & PathSpace::FollowPath : PathSpace::Local; - m_LocalPath = m_Shape->makeCommandPath(localSpace); + m_localPath = artboard()->factory()->makeEmptyRenderPath(); } else { - m_LocalPath->rewind(); + m_localPath->rewind(); + m_localRawPath.rewind(); } - auto world = m_Shape->worldTransform(); + auto world = m_shape->worldTransform(); Mat2D inverseWorld = world.invertOrIdentity(); // Get all the paths into local shape space. - for (auto path : m_Shape->paths()) + for (auto path : m_shape->paths()) { if (!path->isHidden() && !path->isCollapsed()) { const auto localTransform = inverseWorld * path->pathTransform(); - m_LocalPath->addPath(path->commandPath(), localTransform); + m_localRawPath.addPath(path->rawPath(), &localTransform); } } + + // TODO: add a CommandPath::copy(RawPath) + m_localRawPath.addTo(m_localPath.get()); } - if ((space & PathSpace::World) == PathSpace::World) + if (m_shape->isFlagged(PathFlags::world)) { - if (m_WorldPath == nullptr) + if (m_worldPath == nullptr) { - PathSpace worldSpace = - (hasConstraint) ? PathSpace::World & PathSpace::FollowPath : PathSpace::World; - m_WorldPath = m_Shape->makeCommandPath(worldSpace); + m_worldPath = artboard()->factory()->makeEmptyRenderPath(); } else { - m_WorldPath->rewind(); + m_worldPath->rewind(); + m_worldRawPath.rewind(); } - for (auto path : m_Shape->paths()) + for (auto path : m_shape->paths()) { if (!path->isHidden() && !path->isCollapsed()) { const Mat2D& transform = path->pathTransform(); - m_WorldPath->addPath(path->commandPath(), transform); + m_worldRawPath.addPath(path->rawPath(), &transform); } } + // TODO: add a CommandPath::copy(RawPath) + m_worldRawPath.addTo(m_worldPath.get()); } + m_shape->markBoundsDirty(); } } diff --git a/src/shapes/points_path.cpp b/src/shapes/points_path.cpp index a6d70d6c..57953575 100644 --- a/src/shapes/points_path.cpp +++ b/src/shapes/points_path.cpp @@ -28,7 +28,7 @@ const Mat2D& PointsPath::pathTransform() const void PointsPath::update(ComponentDirt value) { - if (hasDirt(value, ComponentDirt::Path) && skin() != nullptr && !m_Shape->canDeferPathUpdate()) + if (hasDirt(value, ComponentDirt::Path) && skin() != nullptr) { // Path tracks re-adding ComponentDirt::Path if we deferred due to to // shape being invisible. @@ -37,7 +37,7 @@ void PointsPath::update(ComponentDirt value) Super::update(value); } -void PointsPath::markPathDirty() +void PointsPath::markPathDirty(bool sendToLayout) { if (skin() != nullptr) { diff --git a/src/shapes/shape.cpp b/src/shapes/shape.cpp index 820b5b1f..68ccf590 100644 --- a/src/shapes/shape.cpp +++ b/src/shapes/shape.cpp @@ -1,6 +1,7 @@ #include "rive/constraints/constraint.hpp" #include "rive/hittest_command_path.hpp" #include "rive/shapes/path.hpp" +#include "rive/shapes/points_path.hpp" #include "rive/shapes/shape.hpp" #include "rive/shapes/clipping_shape.hpp" #include "rive/shapes/paint/blend_mode.hpp" @@ -21,10 +22,25 @@ void Shape::addPath(Path* path) m_Paths.push_back(path); } +void Shape::addFlags(PathFlags flags) { m_pathFlags |= flags; } +bool Shape::isFlagged(PathFlags flags) const { return (int)(pathFlags() & flags) != 0x00; } + bool Shape::canDeferPathUpdate() { - return renderOpacity() == 0 && (pathSpace() & PathSpace::Clipping) != PathSpace::Clipping && - (pathSpace() & PathSpace::FollowPath) != PathSpace::FollowPath; + auto canDefer = + renderOpacity() == 0 && !isFlagged(PathFlags::clipping | PathFlags::neverDeferUpdate); + if (canDefer) + { + // If we have a dependent Skin, don't defer the update + for (auto d : dependents()) + { + if (d->is() && d->as()->skin() != nullptr) + { + return false; + } + } + } + return canDefer; } void Shape::update(ComponentDirt value) @@ -59,8 +75,7 @@ void Shape::pathChanged() void Shape::addToRenderPath(RenderPath* path, const Mat2D& transform) { - auto space = pathSpace(); - if ((space & PathSpace::Local) == PathSpace::Local) + if (isFlagged(PathFlags::local)) { path->addPath(m_PathComposer.localPath(), transform * worldTransform()); } @@ -76,7 +91,7 @@ void Shape::draw(Renderer* renderer) { return; } - ClipResult clipResult = clip(renderer); + ClipResult clipResult = applyClip(renderer); if (clipResult != ClipResult::emptyClip) { @@ -87,14 +102,15 @@ void Shape::draw(Renderer* renderer) continue; } renderer->save(); - bool paintsInLocal = (shapePaint->pathSpace() & PathSpace::Local) == PathSpace::Local; + bool paintsInLocal = shapePaint->isFlagged(PathFlags::local); if (paintsInLocal) { renderer->transform(worldTransform()); } - shapePaint->draw(renderer, - paintsInLocal ? m_PathComposer.localPath() - : m_PathComposer.worldPath()); + shapePaint->draw( + renderer, + paintsInLocal ? m_PathComposer.localPath() : m_PathComposer.worldPath(), + paintsInLocal ? &m_PathComposer.localRawPath() : &m_PathComposer.worldRawPath()); renderer->restore(); } } @@ -114,7 +130,7 @@ bool Shape::hitTest(const IAABB& area) const if (!path->isCollapsed()) { tester.setXform(path->pathTransform()); - path->buildPath(tester); + path->rawPath().addTo(&tester); } } return tester.wasHit(); @@ -129,7 +145,7 @@ Core* Shape::hitTest(HitInfo* hinfo, const Mat2D& xform) // TODO: clip: - const bool shapeIsLocal = (pathSpace() & PathSpace::Local) == PathSpace::Local; + const bool shapeIsLocal = isFlagged(PathFlags::local); for (auto rit = m_ShapePaints.rbegin(); rit != m_ShapePaints.rend(); ++rit) { @@ -143,7 +159,7 @@ Core* Shape::hitTest(HitInfo* hinfo, const Mat2D& xform) continue; } - auto paintIsLocal = (shapePaint->pathSpace() & PathSpace::Local) == PathSpace::Local; + auto paintIsLocal = shapePaint->isFlagged(PathFlags::local); auto mx = xform; if (paintIsLocal) @@ -163,7 +179,7 @@ Core* Shape::hitTest(HitInfo* hinfo, const Mat2D& xform) { tester.setXform(mx * path->pathTransform()); } - path->buildPath(tester); + path->rawPath().addTo(&tester); } if (tester.wasHit()) { @@ -190,8 +206,6 @@ void Shape::buildDependencies() } } -void Shape::addDefaultPathSpace(PathSpace space) { m_DefaultPathSpace |= space; } - StatusCode Shape::onAddedDirty(CoreContext* context) { auto code = Super::onAddedDirty(context); @@ -263,8 +277,7 @@ AABB Shape::computeWorldBounds(const Mat2D* xform) const { continue; } - - path->buildPath(boundsCalculator); + path->rawPath().addTo(&boundsCalculator); AABB aabb = boundsCalculator.bounds(xform == nullptr ? path->pathTransform() : path->pathTransform() * *xform); @@ -289,4 +302,18 @@ AABB Shape::computeLocalBounds() const const Mat2D& world = worldTransform(); Mat2D inverseWorld = world.invertOrIdentity(); return computeWorldBounds(&inverseWorld); +} + +Vec2D Shape::measureLayout(float width, + LayoutMeasureMode widthMode, + float height, + LayoutMeasureMode heightMode) +{ + Vec2D size = Vec2D(); + for (auto path : m_Paths) + { + Vec2D measured = path->measureLayout(width, widthMode, height, heightMode); + size = Vec2D(std::max(size.x, measured.x), std::max(size.y, measured.y)); + } + return size; } \ No newline at end of file diff --git a/src/shapes/shape_paint_container.cpp b/src/shapes/shape_paint_container.cpp index a0232dbd..6a702eb0 100644 --- a/src/shapes/shape_paint_container.cpp +++ b/src/shapes/shape_paint_container.cpp @@ -2,7 +2,7 @@ #include "rive/artboard.hpp" #include "rive/factory.hpp" #include "rive/component.hpp" -#include "rive/shapes/metrics_path.hpp" +#include "rive/layout_component.hpp" #include "rive/shapes/paint/stroke.hpp" #include "rive/shapes/shape.hpp" #include "rive/text/text_style.hpp" @@ -15,6 +15,8 @@ ShapePaintContainer* ShapePaintContainer::from(Component* component) { case Artboard::typeKey: return component->as(); + case LayoutComponent::typeKey: + return component->as(); case Shape::typeKey: return component->as(); case TextStyle::typeKey: @@ -25,12 +27,12 @@ ShapePaintContainer* ShapePaintContainer::from(Component* component) void ShapePaintContainer::addPaint(ShapePaint* paint) { m_ShapePaints.push_back(paint); } -PathSpace ShapePaintContainer::pathSpace() const +PathFlags ShapePaintContainer::pathFlags() const { - PathSpace space = m_DefaultPathSpace; + PathFlags space = m_pathFlags; for (auto paint : m_ShapePaints) { - space |= paint->pathSpace(); + space |= paint->pathFlags(); } return space; } @@ -46,53 +48,6 @@ void ShapePaintContainer::invalidateStrokeEffects() } } -rcp ShapePaintContainer::makeCommandPath(PathSpace space) -{ - // Force a render path if we specifically request to use it for clipping or - // this shape is used for clipping. - bool needForRender = - ((space | m_DefaultPathSpace) & PathSpace::Clipping) == PathSpace::Clipping; - bool needForConstraint = - ((space | m_DefaultPathSpace) & PathSpace::FollowPath) == PathSpace::FollowPath; - - bool needForEffects = false; - - for (auto paint : m_ShapePaints) - { - if (space != PathSpace::Neither && (space & paint->pathSpace()) != space) - { - continue; - } - - if (paint->is() && paint->as()->hasStrokeEffect()) - { - needForEffects = true; - } - else - { - needForRender = true; - } - } - - auto factory = getArtboard()->factory(); - if (needForEffects && needForRender) - { - return make_rcp(factory->makeEmptyRenderPath()); - } - else if (needForConstraint) - { - return make_rcp(factory->makeEmptyRenderPath()); - } - else if (needForEffects) - { - return make_rcp(); - } - else - { - return factory->makeEmptyRenderPath(); - } -} - void ShapePaintContainer::propagateOpacity(float opacity) { for (auto shapePaint : m_ShapePaints) diff --git a/src/text/font_hb.cpp b/src/text/font_hb.cpp index 2bb77e6b..4c477085 100644 --- a/src/text/font_hb.cpp +++ b/src/text/font_hb.cpp @@ -22,6 +22,8 @@ extern "C" // Initialized to null. Client can set this to a callback. rive::Font::FallbackProc rive::Font::gFallbackProc; +bool rive::Font::gFallbackProcEnabled = true; + rive::rcp HBFont::Decode(rive::Span span) { auto blob = hb_blob_create_or_fail((const char*)span.data(), @@ -46,6 +48,10 @@ rive::rcp HBFont::Decode(rive::Span span) return nullptr; } +#ifndef __APPLE__ +rive::rcp HBFont::FromSystem(void* systemFont) { return nullptr; } +#endif + ////////////// constexpr int kStdScale = 2048; @@ -554,7 +560,7 @@ rive::SimpleArray HBFont::onShapeText(rive::Span 0) { diff --git a/src/text/font_hb_apple.mm b/src/text/font_hb_apple.mm new file mode 100644 index 00000000..0277d74d --- /dev/null +++ b/src/text/font_hb_apple.mm @@ -0,0 +1,18 @@ +#include "rive/text_engine.hpp" + +#ifdef WITH_RIVE_TEXT +#include "rive/text/font_hb.hpp" +#import + +#include "hb-coretext.h" + +rive::rcp HBFont::FromSystem(void* systemFont) +{ + auto font = hb_coretext_font_create((CTFontRef)systemFont); + if (font) + { + return rive::rcp(new HBFont(font)); + } + return nullptr; +} +#endif diff --git a/src/text/raw_text.cpp b/src/text/raw_text.cpp new file mode 100644 index 00000000..abc7bba5 --- /dev/null +++ b/src/text/raw_text.cpp @@ -0,0 +1,344 @@ +#ifdef WITH_RIVE_TEXT +#include "rive/text/raw_text.hpp" +#include "rive/text_engine.hpp" +#include "rive/factory.hpp" + +using namespace rive; + +RawText::RawText(Factory* factory) : m_factory(factory) {} +bool RawText::empty() const { return m_styled.empty(); } + +void RawText::append(const std::string& text, + rcp paint, + rcp font, + float size, + float lineHeight, + float letterSpacing) +{ + int styleIndex = 0; + for (RenderStyle& style : m_styles) + { + if (style.paint == paint) + { + break; + } + styleIndex++; + } + if (styleIndex == m_styles.size()) + { + m_styles.push_back({paint, m_factory->makeEmptyRenderPath(), true}); + } + m_styled.append(font, size, lineHeight, letterSpacing, text, styleIndex); + m_dirty = true; +} + +void RawText::clear() +{ + m_styled.clear(); + m_dirty = true; +} + +TextSizing RawText::sizing() const { return m_sizing; } + +TextOverflow RawText::overflow() const { return m_overflow; } + +TextAlign RawText::align() const { return m_align; } + +float RawText::maxWidth() const { return m_maxWidth; } + +float RawText::maxHeight() const { return m_maxHeight; } + +float RawText::paragraphSpacing() const { return m_paragraphSpacing; } + +void RawText::sizing(TextSizing value) +{ + if (m_sizing != value) + { + m_sizing = value; + m_dirty = true; + } +} + +void RawText::overflow(TextOverflow value) +{ + if (m_overflow != value) + { + m_overflow = value; + m_dirty = true; + } +} + +void RawText::align(TextAlign value) +{ + if (m_align != value) + { + m_align = value; + m_dirty = true; + } +} + +void RawText::paragraphSpacing(float value) +{ + if (m_paragraphSpacing != value) + { + m_paragraphSpacing = value; + m_dirty = true; + } +} + +void RawText::maxWidth(float value) +{ + if (m_maxWidth != value) + { + m_maxWidth = value; + m_dirty = true; + } +} + +void RawText::maxHeight(float value) +{ + if (m_maxHeight != value) + { + m_maxHeight = value; + m_dirty = true; + } +} + +void RawText::update() +{ + for (RenderStyle& style : m_styles) + { + style.path->rewind(); + style.isEmpty = true; + } + m_renderStyles.clear(); + if (m_styled.empty()) + { + return; + } + auto runs = m_styled.runs(); + m_shape = runs[0].font->shapeText(m_styled.unichars(), runs); + m_lines = + Text::BreakLines(m_shape, m_sizing == TextSizing::autoWidth ? -1.0f : m_maxWidth, m_align); + + m_orderedLines.clear(); + m_ellipsisRun = {}; + + // build render styles. + if (m_shape.empty()) + { + m_bounds = AABB(0.0f, 0.0f, 0.0f, 0.0f); + return; + } + + // Build up ordered runs as we go. + int paragraphIndex = 0; + float y = 0.0f; + float minY = 0.0f; + float measuredWidth = 0.0f; + if (m_origin == TextOrigin::baseline && !m_lines.empty() && !m_lines[0].empty()) + { + y -= m_lines[0][0].baseline; + minY = y; + } + + int ellipsisLine = -1; + bool isEllipsisLineLast = false; + // Find the line to put the ellipsis on (line before the one that + // overflows). + bool wantEllipsis = m_overflow == TextOverflow::ellipsis && m_sizing == TextSizing::fixed; + + int lastLineIndex = -1; + for (const SimpleArray& paragraphLines : m_lines) + { + const Paragraph& paragraph = m_shape[paragraphIndex++]; + for (const GlyphLine& line : paragraphLines) + { + const GlyphRun& endRun = paragraph.runs[line.endRunIndex]; + const GlyphRun& startRun = paragraph.runs[line.startRunIndex]; + float width = endRun.xpos[line.endGlyphIndex] - startRun.xpos[line.startGlyphIndex] - + endRun.letterSpacing; + if (width > measuredWidth) + { + measuredWidth = width; + } + lastLineIndex++; + if (wantEllipsis && y + line.bottom <= m_maxHeight) + { + ellipsisLine++; + } + } + + if (!paragraphLines.empty()) + { + y += paragraphLines.back().bottom; + } + y += m_paragraphSpacing; + } + if (wantEllipsis && ellipsisLine == -1) + { + // Nothing fits, just show the first line and ellipse it. + ellipsisLine = 0; + } + isEllipsisLineLast = lastLineIndex == ellipsisLine; + + int lineIndex = 0; + paragraphIndex = 0; + switch (m_sizing) + { + case TextSizing::autoWidth: + m_bounds = AABB(0.0f, minY, measuredWidth, std::max(minY, y - m_paragraphSpacing)); + break; + case TextSizing::autoHeight: + m_bounds = AABB(0.0f, minY, m_maxWidth, std::max(minY, y - m_paragraphSpacing)); + break; + case TextSizing::fixed: + m_bounds = AABB(0.0f, minY, m_maxWidth, minY + m_maxHeight); + break; + } + + // Build the clip path if we want it. + if (m_overflow == TextOverflow::clipped) + { + if (m_clipRenderPath == nullptr) + { + m_clipRenderPath = m_factory->makeEmptyRenderPath(); + } + else + { + m_clipRenderPath->rewind(); + } + + m_clipRenderPath->addRect(m_bounds.minX, + m_bounds.minY, + m_bounds.width(), + m_bounds.height()); + } + else + { + m_clipRenderPath = nullptr; + } + + y = 0; + if (m_origin == TextOrigin::baseline && !m_lines.empty() && !m_lines[0].empty()) + { + y -= m_lines[0][0].baseline; + } + paragraphIndex = 0; + + for (const SimpleArray& paragraphLines : m_lines) + { + const Paragraph& paragraph = m_shape[paragraphIndex++]; + for (const GlyphLine& line : paragraphLines) + { + switch (m_overflow) + { + case TextOverflow::hidden: + if (m_sizing == TextSizing::fixed && y + line.bottom > m_maxHeight) + { + return; + } + break; + case TextOverflow::clipped: + if (m_sizing == TextSizing::fixed && y + line.top > m_maxHeight) + { + return; + } + break; + default: + break; + } + + if (lineIndex >= m_orderedLines.size()) + { + // We need to still compute this line's ordered runs. + m_orderedLines.emplace_back(OrderedLine(paragraph, + line, + m_maxWidth, + ellipsisLine == lineIndex, + isEllipsisLineLast, + &m_ellipsisRun)); + } + + const OrderedLine& orderedLine = m_orderedLines[lineIndex]; + float x = line.startX; + float renderY = y + line.baseline; + for (auto glyphItr : orderedLine) + { + const GlyphRun* run = std::get<0>(glyphItr); + size_t glyphIndex = std::get<1>(glyphItr); + + const Font* font = run->font.get(); + const Vec2D& offset = run->offsets[glyphIndex]; + + GlyphID glyphId = run->glyphs[glyphIndex]; + float advance = run->advances[glyphIndex]; + + RawPath path = font->getPath(glyphId); + + path.transformInPlace( + Mat2D(run->size, 0.0f, 0.0f, run->size, x + offset.x, renderY + offset.y)); + + x += advance; + + assert(run->styleId < m_styles.size()); + RenderStyle* style = &m_styles[run->styleId]; + assert(style != nullptr); + path.addTo(style->path.get()); + + if (style->isEmpty) + { + // This was the first path added to the style, so let's mark + // it in our draw list. + style->isEmpty = false; + + m_renderStyles.push_back(style); + } + } + if (lineIndex == ellipsisLine) + { + return; + } + lineIndex++; + } + if (!paragraphLines.empty()) + { + y += paragraphLines.back().bottom; + } + y += m_paragraphSpacing; + } +} + +AABB RawText::bounds() +{ + if (m_dirty) + { + update(); + m_dirty = false; + } + return m_bounds; +} + +void RawText::render(Renderer* renderer, rcp paint) +{ + if (m_dirty) + { + update(); + m_dirty = false; + } + + if (m_overflow == TextOverflow::clipped && m_clipRenderPath) + { + renderer->save(); + renderer->clipPath(m_clipRenderPath.get()); + } + for (auto style : m_renderStyles) + { + renderer->drawPath(style->path.get(), paint ? paint.get() : style->paint.get()); + } + if (m_overflow == TextOverflow::clipped && m_clipRenderPath) + { + renderer->restore(); + } +} +#endif diff --git a/src/text/text.cpp b/src/text/text.cpp index 56bbbfd3..03a47efd 100644 --- a/src/text/text.cpp +++ b/src/text/text.cpp @@ -11,6 +11,7 @@ using namespace rive; #include "rive/artboard.hpp" #include "rive/factory.hpp" #include "rive/clip_result.hpp" +#include void GlyphItr::tryAdvanceRun() { @@ -241,6 +242,26 @@ bool OrderedLine::buildEllipsisRuns(std::vector& logicalRuns, return true; } +Vec2D Text::measureLayout(float width, + LayoutMeasureMode widthMode, + float height, + LayoutMeasureMode heightMode) +{ + return measure(Vec2D( + widthMode == LayoutMeasureMode::undefined ? std::numeric_limits::max() : width, + heightMode == LayoutMeasureMode::undefined ? std::numeric_limits::max() : height)); +} + +void Text::controlSize(Vec2D size) +{ + if (m_layoutWidth != size.x || m_layoutHeight != size.y) + { + m_layoutWidth = size.x; + m_layoutHeight = size.y; + markShapeDirty(false); + } +} + void Text::buildRenderStyles() { for (TextStyle* style : m_renderStyles) @@ -248,7 +269,7 @@ void Text::buildRenderStyles() style->rewindPath(); } m_renderStyles.clear(); - if (m_shape.size() == 0) + if (m_shape.empty()) { m_bounds = AABB(0.0f, 0.0f, 0.0f, 0.0f); return; @@ -270,7 +291,8 @@ void Text::buildRenderStyles() bool isEllipsisLineLast = false; // Find the line to put the ellipsis on (line before the one that // overflows). - bool wantEllipsis = overflow() == TextOverflow::ellipsis && sizing() == TextSizing::fixed; + bool wantEllipsis = + overflow() == TextOverflow::ellipsis && effectiveSizing() == TextSizing::fixed; int lastLineIndex = -1; for (const SimpleArray& paragraphLines : m_lines) @@ -287,7 +309,7 @@ void Text::buildRenderStyles() maxWidth = width; } lastLineIndex++; - if (wantEllipsis && y + line.bottom <= height()) + if (wantEllipsis && y + line.bottom <= effectiveHeight()) { ellipsisLine++; } @@ -308,16 +330,16 @@ void Text::buildRenderStyles() int lineIndex = 0; paragraphIndex = 0; - switch (sizing()) + switch (effectiveSizing()) { case TextSizing::autoWidth: m_bounds = AABB(0.0f, minY, maxWidth, std::max(minY, y - paragraphSpace)); break; case TextSizing::autoHeight: - m_bounds = AABB(0.0f, minY, width(), std::max(minY, y - paragraphSpace)); + m_bounds = AABB(0.0f, minY, effectiveWidth(), std::max(minY, y - paragraphSpace)); break; case TextSizing::fixed: - m_bounds = AABB(0.0f, minY, width(), minY + height()); + m_bounds = AABB(0.0f, minY, effectiveWidth(), minY + effectiveHeight()); break; } @@ -366,13 +388,14 @@ void Text::buildRenderStyles() switch (overflow()) { case TextOverflow::hidden: - if (sizing() == TextSizing::fixed && y + line.bottom > height()) + if (effectiveSizing() == TextSizing::fixed && + y + line.bottom > effectiveHeight()) { return; } break; case TextOverflow::clipped: - if (sizing() == TextSizing::fixed && y + line.top > height()) + if (effectiveSizing() == TextSizing::fixed && y + line.top > effectiveHeight()) { return; } @@ -386,7 +409,7 @@ void Text::buildRenderStyles() // We need to still compute this line's ordered runs. m_orderedLines.emplace_back(OrderedLine(paragraph, line, - width(), + effectiveWidth(), ellipsisLine == lineIndex, isEllipsisLineLast, &m_ellipsisRun)); @@ -485,8 +508,7 @@ const TextStyle* Text::styleFromShaperId(uint16_t id) const void Text::draw(Renderer* renderer) { - - ClipResult clipResult = clip(renderer); + ClipResult clipResult = applyClip(renderer); if (clipResult == ClipResult::noClip) { // We didn't clip, so make sure to save as we'll be doing some @@ -512,7 +534,7 @@ void Text::addRun(TextValueRun* run) { m_runs.push_back(run); } void Text::addModifierGroup(TextModifierGroup* group) { m_modifierGroups.push_back(group); } -void Text::markShapeDirty() +void Text::markShapeDirty(bool sendToLayout) { addDirt(ComponentDirt::Path); for (TextModifierGroup* group : m_modifierGroups) @@ -520,6 +542,18 @@ void Text::markShapeDirty() group->clearRangeMaps(); } markWorldTransformDirty(); +#ifdef WITH_RIVE_LAYOUT + if (sendToLayout) + { + for (ContainerComponent* p = parent(); p != nullptr; p = p->parent()) + { + if (p->is()) + { + p->as()->markLayoutNodeDirty(); + } + } + } +#endif } void Text::modifierShapeDirty() { addDirt(ComponentDirt::Path); } @@ -532,7 +566,7 @@ void Text::sizingValueChanged() { markShapeDirty(); } void Text::overflowValueChanged() { - if (sizing() != TextSizing::autoWidth) + if (effectiveSizing() != TextSizing::autoWidth) { markShapeDirty(); } @@ -540,7 +574,7 @@ void Text::overflowValueChanged() void Text::widthChanged() { - if (sizing() != TextSizing::autoWidth) + if (effectiveSizing() != TextSizing::autoWidth) { markShapeDirty(); } @@ -550,7 +584,7 @@ void Text::paragraphSpacingChanged() { markPaintDirty(); } void Text::heightChanged() { - if (sizing() == TextSizing::fixed) + if (effectiveSizing() == TextSizing::fixed) { markShapeDirty(); } @@ -611,9 +645,9 @@ bool Text::makeStyled(StyledText& styledText, bool withModifiers) const return !styledText.empty(); } -static SimpleArray> breakLines(const SimpleArray& paragraphs, - float width, - TextAlign align) +SimpleArray> Text::BreakLines(const SimpleArray& paragraphs, + float width, + TextAlign align) { bool autoWidth = width == -1.0f; float paragraphWidth = width; @@ -670,9 +704,10 @@ void Text::update(ComponentDirt value) makeStyled(m_modifierStyledText, false); auto runs = m_modifierStyledText.runs(); m_modifierShape = runs[0].font->shapeText(m_modifierStyledText.unichars(), runs); - m_modifierLines = breakLines(m_modifierShape, - sizing() == TextSizing::autoWidth ? -1.0f : width(), - (TextAlign)alignValue()); + m_modifierLines = + BreakLines(m_modifierShape, + effectiveSizing() == TextSizing::autoWidth ? -1.0f : effectiveWidth(), + (TextAlign)alignValue()); m_glyphLookup.compute(m_modifierStyledText.unichars(), m_modifierShape); uint32_t textSize = (uint32_t)m_modifierStyledText.unichars().size(); for (TextModifierGroup* group : m_modifierGroups) @@ -688,9 +723,10 @@ void Text::update(ComponentDirt value) { auto runs = m_styledText.runs(); m_shape = runs[0].font->shapeText(m_styledText.unichars(), runs); - m_lines = breakLines(m_shape, - sizing() == TextSizing::autoWidth ? -1.0f : width(), - (TextAlign)alignValue()); + m_lines = + BreakLines(m_shape, + effectiveSizing() == TextSizing::autoWidth ? -1.0f : effectiveWidth(), + (TextAlign)alignValue()); if (!precomputeModifierCoverage && haveModifiers()) { m_glyphLookup.compute(m_styledText.unichars(), m_shape); @@ -732,6 +768,82 @@ void Text::update(ComponentDirt value) } } +Vec2D Text::measure(Vec2D maxSize) +{ + if (makeStyled(m_styledText)) + { + const float paragraphSpace = paragraphSpacing(); + auto runs = m_styledText.runs(); + auto shape = runs[0].font->shapeText(m_styledText.unichars(), runs); + auto lines = BreakLines(shape, + std::min(maxSize.x, + sizing() == TextSizing::autoWidth + ? std::numeric_limits::max() + : width()), + (TextAlign)alignValue()); + float y = 0; + float computedHeight = 0.0f; + float minY = 0; + int paragraphIndex = 0; + float maxWidth = 0; + + if (textOrigin() == TextOrigin::baseline && !lines.empty() && !lines[0].empty()) + { + y -= m_lines[0][0].baseline; + minY = y; + } + int ellipsisLine = -1; + bool wantEllipsis = overflow() == TextOverflow::ellipsis; + + for (const SimpleArray& paragraphLines : lines) + { + const Paragraph& paragraph = shape[paragraphIndex++]; + for (const GlyphLine& line : paragraphLines) + { + const GlyphRun& endRun = paragraph.runs[line.endRunIndex]; + const GlyphRun& startRun = paragraph.runs[line.startRunIndex]; + float width = endRun.xpos[line.endGlyphIndex] - + startRun.xpos[line.startGlyphIndex] - endRun.letterSpacing; + if (width > maxWidth) + { + maxWidth = width; + } + if (wantEllipsis && y + line.bottom > maxSize.y) + { + if (ellipsisLine == -1) + { + // Nothing fits, just show the first line and ellipse it. + computedHeight = y + line.bottom; + } + goto doneMeasuring; + } + ellipsisLine++; + computedHeight = y + line.bottom; + } + if (!paragraphLines.empty()) + { + y += paragraphLines.back().bottom; + } + y += paragraphSpace; + } + doneMeasuring: + + switch (sizing()) + { + case TextSizing::autoWidth: + return Vec2D(maxWidth, std::max(minY, computedHeight)); + break; + case TextSizing::autoHeight: + return Vec2D(width(), std::max(minY, computedHeight)); + break; + case TextSizing::fixed: + return Vec2D(width(), minY + height()); + break; + } + } + return Vec2D(); +} + AABB Text::localBounds() const { float width = m_bounds.width(); @@ -775,7 +887,7 @@ void Text::draw(Renderer* renderer) {} Core* Text::hitTest(HitInfo*, const Mat2D&) { return nullptr; } void Text::addRun(TextValueRun* run) {} void Text::addModifierGroup(TextModifierGroup* group) {} -void Text::markShapeDirty() {} +void Text::markShapeDirty(bool sendToLayout) {} void Text::update(ComponentDirt value) {} void Text::alignValueChanged() {} void Text::sizingValueChanged() {} @@ -791,4 +903,12 @@ AABB Text::localBounds() const { return AABB(); } void Text::originValueChanged() {} void Text::originXChanged() {} void Text::originYChanged() {} +Vec2D Text::measureLayout(float width, + LayoutMeasureMode widthMode, + float height, + LayoutMeasureMode heightMode) +{ + return Vec2D(); +} +void Text::controlSize(Vec2D size) {} #endif \ No newline at end of file diff --git a/src/text/text_style.cpp b/src/text/text_style.cpp index e1b13434..a2a81eae 100644 --- a/src/text/text_style.cpp +++ b/src/text/text_style.cpp @@ -191,7 +191,7 @@ void TextStyle::draw(Renderer* renderer) { RenderPaint* renderPaint = m_paintPool[paintIndex++].get(); shapePaint->applyTo(renderPaint, itr->first); - shapePaint->draw(renderer, itr->second.get(), renderPaint); + shapePaint->draw(renderer, itr->second.get(), nullptr, renderPaint); } } } diff --git a/src/transform_component.cpp b/src/transform_component.cpp index 0e9c1ab9..73bc031b 100644 --- a/src/transform_component.cpp +++ b/src/transform_component.cpp @@ -79,7 +79,11 @@ void TransformComponent::updateWorldTransform() { m_WorldTransform = m_Transform; } + updateConstraints(); +} +void TransformComponent::updateConstraints() +{ for (auto constraint : m_Constraints) { constraint->constrain(this); diff --git a/src/viewmodel/data_enum.cpp b/src/viewmodel/data_enum.cpp new file mode 100644 index 00000000..a1780cb1 --- /dev/null +++ b/src/viewmodel/data_enum.cpp @@ -0,0 +1,80 @@ +#include +#include +#include + +#include "rive/viewmodel/data_enum.hpp" +#include "rive/viewmodel/data_enum_value.hpp" +#include "rive/viewmodel/viewmodel_property.hpp" +#include "rive/backboard.hpp" +#include "rive/importers/backboard_importer.hpp" + +using namespace rive; + +void DataEnum::addValue(DataEnumValue* value) { m_Values.push_back(value); } + +std::string DataEnum::value(std::string key) +{ + for (auto enumValue : m_Values) + { + if (enumValue->key() == key) + { + return enumValue->value(); + }; + } + return ""; +} + +std::string DataEnum::value(uint32_t index) +{ + if (index < m_Values.size()) + { + return m_Values[index]->value(); + } + return ""; +} + +bool DataEnum::value(std::string key, std::string value) +{ + for (auto enumValue : m_Values) + { + if (enumValue->key() == key) + { + enumValue->value(value); + return true; + }; + } + return false; +} + +bool DataEnum::value(uint32_t index, std::string value) +{ + if (index < m_Values.size()) + { + m_Values[index]->value(value); + return true; + } + return false; +} + +int DataEnum::valueIndex(std::string key) +{ + int index = 0; + for (auto enumValue : m_Values) + { + if (enumValue->key() == key) + { + return index; + }; + index++; + } + return -1; +} + +int DataEnum::valueIndex(uint32_t index) +{ + if (index < m_Values.size()) + { + return index; + } + return -1; +} \ No newline at end of file diff --git a/src/viewmodel/data_enum_value.cpp b/src/viewmodel/data_enum_value.cpp new file mode 100644 index 00000000..e5a2e90e --- /dev/null +++ b/src/viewmodel/data_enum_value.cpp @@ -0,0 +1,21 @@ +#include +#include +#include + +#include "rive/viewmodel/data_enum.hpp" +#include "rive/viewmodel/data_enum_value.hpp" +#include "rive/importers/enum_importer.hpp" + +using namespace rive; + +StatusCode DataEnumValue::import(ImportStack& importStack) +{ + auto enumImporter = importStack.latest(DataEnum::typeKey); + if (enumImporter == nullptr) + { + return StatusCode::MissingObject; + } + + enumImporter->addValue(this); + return Super::import(importStack); +} \ No newline at end of file diff --git a/src/viewmodel/viewmodel.cpp b/src/viewmodel/viewmodel.cpp new file mode 100644 index 00000000..3d9ecfa2 --- /dev/null +++ b/src/viewmodel/viewmodel.cpp @@ -0,0 +1,62 @@ +#include +#include +#include + +#include "rive/viewmodel/viewmodel.hpp" +#include "rive/viewmodel/viewmodel_property.hpp" +#include "rive/backboard.hpp" +#include "rive/importers/backboard_importer.hpp" + +using namespace rive; + +void ViewModel::addProperty(ViewModelProperty* property) { m_Properties.push_back(property); } + +ViewModelProperty* ViewModel::property(size_t index) +{ + if (index < m_Properties.size()) + { + return m_Properties[index]; + } + return nullptr; +} + +ViewModelProperty* ViewModel::property(const std::string& name) +{ + for (auto property : m_Properties) + { + if (property->name() == name) + { + return property; + } + } + return nullptr; +} + +void ViewModel::addInstance(ViewModelInstance* value) +{ + m_Instances.push_back(value); + value->viewModel(this); +} + +ViewModelInstance* ViewModel::defaultInstance() { return m_Instances[defaultInstanceId()]; } + +ViewModelInstance* ViewModel::instance(size_t index) +{ + if (index < m_Instances.size()) + { + return m_Instances[index]; + } + return nullptr; +} + +ViewModelInstance* ViewModel::instance(const std::string& name) +{ + for (auto instance : m_Instances) + { + if (instance->name() == name) + { + return instance; + } + } + return nullptr; +} \ No newline at end of file diff --git a/src/viewmodel/viewmodel_instance.cpp b/src/viewmodel/viewmodel_instance.cpp new file mode 100644 index 00000000..b2656b52 --- /dev/null +++ b/src/viewmodel/viewmodel_instance.cpp @@ -0,0 +1,115 @@ +#include +#include +#include + +#include "rive/viewmodel/viewmodel_instance.hpp" +#include "rive/viewmodel/viewmodel.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" +#include "rive/viewmodel/viewmodel_instance_viewmodel.hpp" +#include "rive/importers/viewmodel_importer.hpp" +#include "rive/core_context.hpp" + +using namespace rive; + +void ViewModelInstance::addValue(ViewModelInstanceValue* value) +{ + m_PropertyValues.push_back(value); +} + +ViewModelInstanceValue* ViewModelInstance::propertyValue(const uint32_t id) +{ + for (auto value : m_PropertyValues) + { + if (value->viewModelPropertyId() == id) + { + return value; + } + } + return nullptr; +} + +ViewModelInstanceValue* ViewModelInstance::propertyValue(const std::string& name) +{ + auto viewModelProperty = viewModel()->property(name); + if (viewModelProperty != nullptr) + { + for (auto value : m_PropertyValues) + { + if (value->viewModelProperty() == viewModelProperty) + { + return value; + } + } + } + return nullptr; +} + +void ViewModelInstance::viewModel(ViewModel* value) { m_ViewModel = value; } + +ViewModel* ViewModelInstance::viewModel() const { return m_ViewModel; } + +void ViewModelInstance::onComponentDirty(Component* component) {} + +void ViewModelInstance::setAsRoot() { setRoot(this); } + +void ViewModelInstance::setRoot(ViewModelInstance* value) +{ + for (auto propertyValue : m_PropertyValues) + { + propertyValue->setRoot(value); + } +} + +std::vector ViewModelInstance::propertyValues() +{ + return m_PropertyValues; +} + +Core* ViewModelInstance::clone() const +{ + auto cloned = new ViewModelInstance(); + cloned->copy(*this); + for (auto propertyValue : m_PropertyValues) + { + auto clonedValue = propertyValue->clone()->as(); + cloned->addValue(clonedValue); + } + cloned->viewModel(viewModel()); + return cloned; +} + +StatusCode ViewModelInstance::import(ImportStack& importStack) +{ + auto viewModelImporter = importStack.latest(ViewModel::typeKey); + if (viewModelImporter == nullptr) + { + return StatusCode::MissingObject; + } + + viewModelImporter->addInstance(this); + return StatusCode::Ok; +} + +ViewModelInstanceValue* ViewModelInstance::propertyFromPath(std::vector* path, + size_t index) +{ + if (index < path->size()) + { + auto propertyId = (*path)[index]; + auto property = propertyValue(propertyId); + if (property != nullptr) + { + if (index == path->size() - 1) + { + return property; + } + if (property->is()) + { + auto propertyViewModel = property->as(); + auto viewModelInstance = propertyViewModel->referenceViewModelInstance(); + return viewModelInstance->propertyFromPath(path, index + 1); + } + } + } + return nullptr; +} \ No newline at end of file diff --git a/src/viewmodel/viewmodel_instance_boolean.cpp b/src/viewmodel/viewmodel_instance_boolean.cpp new file mode 100644 index 00000000..c4e55996 --- /dev/null +++ b/src/viewmodel/viewmodel_instance_boolean.cpp @@ -0,0 +1,10 @@ +#include +#include +#include + +#include "rive/viewmodel/viewmodel_instance_boolean.hpp" +#include "rive/component_dirt.hpp" + +using namespace rive; + +void ViewModelInstanceBoolean::propertyValueChanged() { addDirt(ComponentDirt::Bindings); } \ No newline at end of file diff --git a/src/viewmodel/viewmodel_instance_color.cpp b/src/viewmodel/viewmodel_instance_color.cpp new file mode 100644 index 00000000..9cd82bd1 --- /dev/null +++ b/src/viewmodel/viewmodel_instance_color.cpp @@ -0,0 +1,10 @@ +#include +#include +#include + +#include "rive/viewmodel/viewmodel_instance_color.hpp" +#include "rive/component_dirt.hpp" + +using namespace rive; + +void ViewModelInstanceColor::propertyValueChanged() { addDirt(ComponentDirt::Bindings); } \ No newline at end of file diff --git a/src/viewmodel/viewmodel_instance_enum.cpp b/src/viewmodel/viewmodel_instance_enum.cpp new file mode 100644 index 00000000..3b7a7556 --- /dev/null +++ b/src/viewmodel/viewmodel_instance_enum.cpp @@ -0,0 +1,34 @@ +#include +#include +#include + +#include "rive/viewmodel/viewmodel_instance_enum.hpp" +#include "rive/viewmodel/viewmodel_property_enum.hpp" +#include "rive/component_dirt.hpp" + +using namespace rive; + +void ViewModelInstanceEnum::propertyValueChanged() { addDirt(ComponentDirt::Bindings); } + +bool ViewModelInstanceEnum::value(std::string name) +{ + auto enumProperty = viewModelProperty()->as(); + int index = enumProperty->valueIndex(name); + if (index != -1) + { + propertyValue(index); + return true; + } + return false; +} + +bool ViewModelInstanceEnum::value(uint32_t index) +{ + auto enumProperty = viewModelProperty()->as(); + if (enumProperty->valueIndex(index) != -1) + { + propertyValue(index); + return true; + } + return false; +} \ No newline at end of file diff --git a/src/viewmodel/viewmodel_instance_list.cpp b/src/viewmodel/viewmodel_instance_list.cpp new file mode 100644 index 00000000..0ac9f5a1 --- /dev/null +++ b/src/viewmodel/viewmodel_instance_list.cpp @@ -0,0 +1,74 @@ +#include +#include +#include + +#include "rive/viewmodel/viewmodel_instance_list.hpp" +#include "rive/viewmodel/viewmodel_instance_list_item.hpp" +#include "rive/component_dirt.hpp" + +using namespace rive; + +void ViewModelInstanceList::propertyValueChanged() { addDirt(ComponentDirt::Components); } + +void ViewModelInstanceList::addItem(ViewModelInstanceListItem* item) +{ + m_ListItems.push_back(item); + propertyValueChanged(); +} + +void ViewModelInstanceList::insertItem(int index, ViewModelInstanceListItem* item) +{ + // TODO: @hernan decide if we want to return a boolean + if (index < m_ListItems.size()) + { + m_ListItems.insert(m_ListItems.begin() + index, item); + propertyValueChanged(); + } +} + +void ViewModelInstanceList::removeItem(int index) +{ + // TODO: @hernan decide if we want to return a boolean + if (index < m_ListItems.size()) + { + m_ListItems.erase(m_ListItems.begin() + index); + propertyValueChanged(); + } +} + +void ViewModelInstanceList::removeItem(ViewModelInstanceListItem* listItem) +{ + auto noSpaceEnd = std::remove(m_ListItems.begin(), m_ListItems.end(), listItem); + m_ListItems.erase(noSpaceEnd, m_ListItems.end()); + propertyValueChanged(); +} + +ViewModelInstanceListItem* ViewModelInstanceList::item(uint32_t index) +{ + if (index < m_ListItems.size()) + { + return m_ListItems[index]; + } + return nullptr; +} + +void ViewModelInstanceList::swap(uint32_t index1, uint32_t index2) +{ + if (index1 < m_ListItems.size() && index2 < m_ListItems.size()) + { + std::iter_swap(m_ListItems.begin() + index1, m_ListItems.begin() + index2); + propertyValueChanged(); + } +} + +Core* ViewModelInstanceList::clone() const +{ + auto cloned = new ViewModelInstanceList(); + cloned->copy(*this); + for (auto property : m_ListItems) + { + auto clonedValue = property->clone()->as(); + cloned->addItem(clonedValue); + } + return cloned; +} \ No newline at end of file diff --git a/src/viewmodel/viewmodel_instance_list_item.cpp b/src/viewmodel/viewmodel_instance_list_item.cpp new file mode 100644 index 00000000..a723c6c7 --- /dev/null +++ b/src/viewmodel/viewmodel_instance_list_item.cpp @@ -0,0 +1,22 @@ +#include +#include +#include + +#include "rive/viewmodel/viewmodel_instance_list.hpp" +#include "rive/viewmodel/viewmodel_instance_list_item.hpp" +#include "rive/importers/viewmodel_instance_list_importer.hpp" + +using namespace rive; + +StatusCode ViewModelInstanceListItem::import(ImportStack& importStack) +{ + auto viewModelInstanceList = + importStack.latest(ViewModelInstanceList::typeKey); + if (viewModelInstanceList == nullptr) + { + return StatusCode::MissingObject; + } + viewModelInstanceList->addItem(this); + + return Super::import(importStack); +} \ No newline at end of file diff --git a/src/viewmodel/viewmodel_instance_number.cpp b/src/viewmodel/viewmodel_instance_number.cpp new file mode 100644 index 00000000..6e2620a6 --- /dev/null +++ b/src/viewmodel/viewmodel_instance_number.cpp @@ -0,0 +1,10 @@ +#include +#include +#include + +#include "rive/viewmodel/viewmodel_instance_number.hpp" +#include "rive/component_dirt.hpp" + +using namespace rive; + +void ViewModelInstanceNumber::propertyValueChanged() { addDirt(ComponentDirt::Bindings); } \ No newline at end of file diff --git a/src/viewmodel/viewmodel_instance_string.cpp b/src/viewmodel/viewmodel_instance_string.cpp new file mode 100644 index 00000000..6c60d4b8 --- /dev/null +++ b/src/viewmodel/viewmodel_instance_string.cpp @@ -0,0 +1,10 @@ +#include +#include +#include + +#include "rive/viewmodel/viewmodel_instance_string.hpp" +#include "rive/component_dirt.hpp" + +using namespace rive; + +void ViewModelInstanceString::propertyValueChanged() { addDirt(ComponentDirt::Bindings); } \ No newline at end of file diff --git a/src/viewmodel/viewmodel_instance_value.cpp b/src/viewmodel/viewmodel_instance_value.cpp new file mode 100644 index 00000000..de7bdba0 --- /dev/null +++ b/src/viewmodel/viewmodel_instance_value.cpp @@ -0,0 +1,41 @@ +#include +#include +#include + +#include "rive/viewmodel/viewmodel_instance.hpp" +#include "rive/viewmodel/viewmodel_instance_value.hpp" +#include "rive/importers/viewmodel_instance_importer.hpp" +#include "rive/data_bind/data_bind.hpp" + +using namespace rive; + +StatusCode ViewModelInstanceValue::import(ImportStack& importStack) +{ + auto viewModelInstanceImporter = + importStack.latest(ViewModelInstance::typeKey); + if (viewModelInstanceImporter == nullptr) + { + return StatusCode::MissingObject; + } + viewModelInstanceImporter->addValue(this); + + return Super::import(importStack); +} + +void ViewModelInstanceValue::viewModelProperty(ViewModelProperty* value) +{ + m_ViewModelProperty = value; +} +ViewModelProperty* ViewModelInstanceValue::viewModelProperty() { return m_ViewModelProperty; } + +void ViewModelInstanceValue::addDependent(DataBind* value) +{ + m_DependencyHelper.addDependent(value); +} + +void ViewModelInstanceValue::addDirt(ComponentDirt value) { m_DependencyHelper.addDirt(value); } + +void ViewModelInstanceValue::setRoot(ViewModelInstance* viewModelInstance) +{ + m_DependencyHelper.dependecyRoot(viewModelInstance); +} \ No newline at end of file diff --git a/src/viewmodel/viewmodel_instance_viewmodel.cpp b/src/viewmodel/viewmodel_instance_viewmodel.cpp new file mode 100644 index 00000000..7630112e --- /dev/null +++ b/src/viewmodel/viewmodel_instance_viewmodel.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +#include "rive/viewmodel/viewmodel_instance_viewmodel.hpp" + +using namespace rive; + +void ViewModelInstanceViewModel::setRoot(ViewModelInstance* value) +{ + Super::setRoot(value); + referenceViewModelInstance()->setRoot(value); +} \ No newline at end of file diff --git a/src/viewmodel/viewmodel_property.cpp b/src/viewmodel/viewmodel_property.cpp new file mode 100644 index 00000000..4450bc16 --- /dev/null +++ b/src/viewmodel/viewmodel_property.cpp @@ -0,0 +1,17 @@ +#include "rive/viewmodel/viewmodel_property.hpp" +#include "rive/importers/viewmodel_importer.hpp" +#include "rive/viewmodel/viewmodel.hpp" + +using namespace rive; + +StatusCode ViewModelProperty::import(ImportStack& importStack) +{ + auto viewModelImporter = importStack.latest(ViewModel::typeKey); + if (viewModelImporter == nullptr) + { + return StatusCode::MissingObject; + } + + viewModelImporter->addProperty(this); + return Super::import(importStack); +} diff --git a/src/viewmodel/viewmodel_property_enum.cpp b/src/viewmodel/viewmodel_property_enum.cpp new file mode 100644 index 00000000..ef5e12f8 --- /dev/null +++ b/src/viewmodel/viewmodel_property_enum.cpp @@ -0,0 +1,61 @@ +#include "rive/viewmodel/viewmodel_property_enum.hpp" + +using namespace rive; + +void ViewModelPropertyEnum::dataEnum(DataEnum* value) { m_DataEnum = value; } + +DataEnum* ViewModelPropertyEnum::dataEnum() { return m_DataEnum; } + +std::string ViewModelPropertyEnum::value(std::string name) +{ + if (dataEnum() != nullptr) + { + return dataEnum()->value(name); + } + return ""; +} + +std::string ViewModelPropertyEnum::value(uint32_t index) +{ + if (dataEnum() != nullptr) + { + return dataEnum()->value(index); + } + return ""; +} + +bool ViewModelPropertyEnum::value(std::string name, std::string value) +{ + if (dataEnum() != nullptr) + { + return dataEnum()->value(name, value); + } + return false; +} + +bool ViewModelPropertyEnum::value(uint32_t index, std::string value) +{ + if (dataEnum() != nullptr) + { + return dataEnum()->value(index, value); + } + return false; +} + +int ViewModelPropertyEnum::valueIndex(std::string name) +{ + if (dataEnum() != nullptr) + { + return dataEnum()->valueIndex(name); + } + return -1; +} + +int ViewModelPropertyEnum::valueIndex(uint32_t index) +{ + if (dataEnum() != nullptr) + { + return dataEnum()->valueIndex(index); + } + return -1; +} diff --git a/tess/build/premake5_tess.lua b/tess/build/premake5_tess.lua index 7a07170e..7f93b186 100644 --- a/tess/build/premake5_tess.lua +++ b/tess/build/premake5_tess.lua @@ -21,6 +21,7 @@ do dependencies .. '/sokol', dependencies .. '/earcut.hpp/include/mapbox', dependencies .. '/libtess2/Include', + yoga, }) files({ '../src/**.cpp', dependencies .. '/libtess2/Source/**.c' }) buildoptions({ '-Wall', '-fno-exceptions', '-fno-rtti', '-Werror=format' }) @@ -79,11 +80,12 @@ do rive .. '/include', dependencies .. '/sokol', dependencies .. '/earcut.hpp/include/mapbox', + yoga, }) files({ '../test/**.cpp', rive .. 'utils/no_op_factory.cpp' }) - links({ 'rive_tess_renderer', 'rive', 'rive_harfbuzz', 'rive_sheenbidi' }) + links({ 'rive_tess_renderer', 'rive', 'rive_harfbuzz', 'rive_sheenbidi', 'rive_yoga' }) buildoptions({ '-Wall', '-fno-exceptions', '-fno-rtti', '-Werror=format' }) - defines({ 'TESTING' }) + defines({ 'TESTING', 'YOGA_EXPORT=' }) filter('configurations:debug') do diff --git a/tess/test/triangulation_test.cpp b/tess/test/triangulation_test.cpp index 58376354..d2a4b018 100644 --- a/tess/test/triangulation_test.cpp +++ b/tess/test/triangulation_test.cpp @@ -29,7 +29,7 @@ TEST_CASE("simple triangle path triangulates as expected", "[file]") auto path = artboard->find("triangle_path"); REQUIRE(path != nullptr); TestRenderPath renderPath; - path->buildPath(renderPath); + path->rawPath().addTo(&renderPath); rive::Mat2D identity; TestRenderPath shapeRenderPath; diff --git a/test/aabb_test.cpp b/test/aabb_test.cpp index 4adafea3..2e380b3b 100644 --- a/test/aabb_test.cpp +++ b/test/aabb_test.cpp @@ -29,4 +29,39 @@ TEST_CASE("IAABB_empty", "[IAABB]") std::numeric_limits::min()} .empty()); } + +TEST_CASE("isEmptyOrNaN", "[AABB]") +{ + auto inf = std::numeric_limits::infinity(); + auto nan = std::numeric_limits::quiet_NaN(); + CHECK(!AABB{0, 0, 1, 1}.isEmptyOrNaN()); + CHECK(!AABB{-inf, -inf, inf, inf}.isEmptyOrNaN()); + CHECK(AABB{0, 0, 0, 0}.isEmptyOrNaN()); + CHECK(AABB{0, 0, -1, -2}.isEmptyOrNaN()); + CHECK(AABB{inf, inf, -inf, -inf}.isEmptyOrNaN()); + CHECK(AABB{inf, -inf, -inf, inf}.isEmptyOrNaN()); + CHECK(AABB{-inf, inf, inf, -inf}.isEmptyOrNaN()); + CHECK(AABB{nan, 0, 10, 10}.isEmptyOrNaN()); + CHECK(AABB{0, nan, 10, 10}.isEmptyOrNaN()); + CHECK(AABB{0, 0, nan, 10}.isEmptyOrNaN()); + CHECK(AABB{0, 0, 10, nan}.isEmptyOrNaN()); + CHECK(AABB{nan, nan, 10, 10}.isEmptyOrNaN()); + CHECK(AABB{nan, nan, nan, 10}.isEmptyOrNaN()); + CHECK(AABB{nan, nan, nan, nan}.isEmptyOrNaN()); +} + +TEST_CASE("AABB contains", "[AABB]") +{ + CHECK(AABB{0, 0, 100, 100}.contains(Vec2D(20, 20))); + CHECK(AABB{0, 0, 100, 100}.contains(Vec2D(0, 0))); + CHECK(AABB{0, 0, 100, 100}.contains(Vec2D(100, 100))); + CHECK(!AABB{0, 0, 100, 100}.contains(Vec2D(200, 200))); + CHECK(!AABB{0, 0, 100, 100}.contains(Vec2D(-200, -200))); + auto leftBoundary = 0.f; + auto rightBoundary = 100.f; + CHECK(!AABB{leftBoundary, 0, rightBoundary, 100.0}.contains( + Vec2D(leftBoundary - std::numeric_limits::epsilon(), 50))); + CHECK(!AABB{leftBoundary, 0, rightBoundary, 100.0}.contains( + Vec2D(rightBoundary + rightBoundary * std::numeric_limits::epsilon(), 50))); +} } // namespace rive diff --git a/test/animation_state_instance_test.cpp b/test/animation_state_instance_test.cpp index 29b85221..918f2e41 100644 --- a/test/animation_state_instance_test.cpp +++ b/test/animation_state_instance_test.cpp @@ -256,6 +256,243 @@ TEST_CASE("AnimationStateInstance with negative speed starts a negative animatio // backwards 2 seconds from 5. REQUIRE(animationStateInstance->animationInstance()->time() == 0.0); + delete animationStateInstance; + delete animationState; + delete linearAnimation; +} + +TEST_CASE("AnimationStateInstance spilledTime accounts for Nx speed with oneShot", "[animation]") +{ + + rive::NoOpFactory emptyFactory; + // For each of these tests, we cons up a dummy artboard/instance + // just to make the animations happy. + rive::Artboard ab(&emptyFactory); + auto abi = ab.instance(); + + rive::StateMachine machine; + rive::StateMachineInstance stateMachineInstance(&machine, abi.get()); + + rive::LinearAnimation* linearAnimation = new rive::LinearAnimation(); + // duration in seconds is 2 + linearAnimation->duration(4); + linearAnimation->fps(2); + linearAnimation->speed(2); + linearAnimation->loopValue(static_cast(rive::Loop::oneShot)); + + rive::AnimationState* animationState = new rive::AnimationState(); + animationState->animation(linearAnimation); + + rive::AnimationStateInstance* animationStateInstance = + new rive::AnimationStateInstance(animationState, abi.get()); + + // play from beginning. + animationStateInstance->advance(3.0, &stateMachineInstance); + + REQUIRE(animationStateInstance->animationInstance()->time() == 2.0); + REQUIRE(animationStateInstance->animationInstance()->totalTime() == 6.0); + // Duration is 2s but at a 2x speed it takes 1s to end + // When advancing 3s, there are still 2s remaining (spilled) + REQUIRE(animationStateInstance->animationInstance()->spilledTime() == 2.0); + + delete animationStateInstance; + delete animationState; + delete linearAnimation; +} + +TEST_CASE("AnimationStateInstance spilledTime accounts for 1/Nx speed with oneShot", "[animation]") +{ + + rive::NoOpFactory emptyFactory; + // For each of these tests, we cons up a dummy artboard/instance + // just to make the animations happy. + rive::Artboard ab(&emptyFactory); + auto abi = ab.instance(); + + rive::StateMachine machine; + rive::StateMachineInstance stateMachineInstance(&machine, abi.get()); + + rive::LinearAnimation* linearAnimation = new rive::LinearAnimation(); + // duration in seconds is 2 + linearAnimation->duration(4); + linearAnimation->fps(2); + linearAnimation->speed(0.5); + linearAnimation->loopValue(static_cast(rive::Loop::oneShot)); + + rive::AnimationState* animationState = new rive::AnimationState(); + animationState->animation(linearAnimation); + + rive::AnimationStateInstance* animationStateInstance = + new rive::AnimationStateInstance(animationState, abi.get()); + + // play from beginning. + animationStateInstance->advance(5.0, &stateMachineInstance); + + REQUIRE(animationStateInstance->animationInstance()->time() == 2.0); + REQUIRE(animationStateInstance->animationInstance()->totalTime() == 2.5); + // Duration is 2s but at a 0.5x speed it takes 4s to end + // When advancing 5.0s, there are still 1s remaining (spilled) + REQUIRE(animationStateInstance->animationInstance()->spilledTime() == 1.0); + + delete animationStateInstance; + delete animationState; + delete linearAnimation; +} + +TEST_CASE("AnimationStateInstance spilledTime accounts for Nx speed with loop", "[animation]") +{ + + rive::NoOpFactory emptyFactory; + // For each of these tests, we cons up a dummy artboard/instance + // just to make the animations happy. + rive::Artboard ab(&emptyFactory); + auto abi = ab.instance(); + + rive::StateMachine machine; + rive::StateMachineInstance stateMachineInstance(&machine, abi.get()); + + rive::LinearAnimation* linearAnimation = new rive::LinearAnimation(); + // duration in seconds is 2 + linearAnimation->duration(4); + linearAnimation->fps(2); + linearAnimation->speed(2); + linearAnimation->loopValue(static_cast(rive::Loop::loop)); + + rive::AnimationState* animationState = new rive::AnimationState(); + animationState->animation(linearAnimation); + + rive::AnimationStateInstance* animationStateInstance = + new rive::AnimationStateInstance(animationState, abi.get()); + + // play from beginning. + animationStateInstance->advance(5.5, &stateMachineInstance); + + REQUIRE(animationStateInstance->animationInstance()->time() == 1.0); + REQUIRE(animationStateInstance->animationInstance()->totalTime() == 11.0); + // Duration is 2s but at a 2x speed it takes 1s to loop + // When advancing 5.5s, there is still 0.5s remaining (spilled) + REQUIRE(animationStateInstance->animationInstance()->spilledTime() == 0.5); + + delete animationStateInstance; + delete animationState; + delete linearAnimation; +} + +TEST_CASE("AnimationStateInstance spilledTime accounts for 1/Nx speed with loop", "[animation]") +{ + rive::NoOpFactory emptyFactory; + // For each of these tests, we cons up a dummy artboard/instance + // just to make the animations happy. + rive::Artboard ab(&emptyFactory); + auto abi = ab.instance(); + + rive::StateMachine machine; + rive::StateMachineInstance stateMachineInstance(&machine, abi.get()); + + rive::LinearAnimation* linearAnimation = new rive::LinearAnimation(); + // duration in seconds is 2 + linearAnimation->duration(4); + linearAnimation->fps(2); + linearAnimation->speed(0.5); + linearAnimation->loopValue(static_cast(rive::Loop::loop)); + + rive::AnimationState* animationState = new rive::AnimationState(); + animationState->animation(linearAnimation); + + rive::AnimationStateInstance* animationStateInstance = + new rive::AnimationStateInstance(animationState, abi.get()); + + // play from beginning. + animationStateInstance->advance(10.0, &stateMachineInstance); + + REQUIRE(animationStateInstance->animationInstance()->time() == 1.0); + REQUIRE(animationStateInstance->animationInstance()->totalTime() == 5.0); + // Duration is 2s but at a 2x speed it takes 1s to loop + // When advancing 5.5s, there is still 0.5s remaining (spilled) + REQUIRE(animationStateInstance->animationInstance()->spilledTime() == 2.0); + + delete animationStateInstance; + delete animationState; + delete linearAnimation; +} + +TEST_CASE("AnimationStateInstance spilledTime accounts for -Nx speed with oneShot", "[animation]") +{ + + rive::NoOpFactory emptyFactory; + // For each of these tests, we cons up a dummy artboard/instance + // just to make the animations happy. + rive::Artboard ab(&emptyFactory); + auto abi = ab.instance(); + + rive::StateMachine machine; + rive::StateMachineInstance stateMachineInstance(&machine, abi.get()); + + rive::LinearAnimation* linearAnimation = new rive::LinearAnimation(); + // duration in seconds is 2 + linearAnimation->duration(4); + linearAnimation->fps(2); + linearAnimation->speed(-2); + linearAnimation->loopValue(static_cast(rive::Loop::oneShot)); + + rive::AnimationState* animationState = new rive::AnimationState(); + animationState->animation(linearAnimation); + + rive::AnimationStateInstance* animationStateInstance = + new rive::AnimationStateInstance(animationState, abi.get()); + + // play from beginning. + animationStateInstance->advance(3.0, &stateMachineInstance); + + REQUIRE(animationStateInstance->animationInstance()->time() == 0.0); + REQUIRE(animationStateInstance->animationInstance()->totalTime() == 6.0); + // Duration is 2s but at a -2x speed it takes 1s to end + // When advancing at negative speed, time starts at duration + // so starting at end and taking 1s to complete + // there are still 2s remaining (spilled) + REQUIRE(animationStateInstance->animationInstance()->spilledTime() == 2.0); + + delete animationStateInstance; + delete animationState; + delete linearAnimation; +} + +TEST_CASE("AnimationStateInstance spilledTime accounts for -Nx speed with loop", "[animation]") +{ + + rive::NoOpFactory emptyFactory; + // For each of these tests, we cons up a dummy artboard/instance + // just to make the animations happy. + rive::Artboard ab(&emptyFactory); + auto abi = ab.instance(); + + rive::StateMachine machine; + rive::StateMachineInstance stateMachineInstance(&machine, abi.get()); + + rive::LinearAnimation* linearAnimation = new rive::LinearAnimation(); + // duration in seconds is 2 + linearAnimation->duration(4); + linearAnimation->fps(2); + linearAnimation->speed(-2); + linearAnimation->loopValue(static_cast(rive::Loop::loop)); + + rive::AnimationState* animationState = new rive::AnimationState(); + animationState->animation(linearAnimation); + + rive::AnimationStateInstance* animationStateInstance = + new rive::AnimationStateInstance(animationState, abi.get()); + + // play from beginning. + animationStateInstance->advance(5.5, &stateMachineInstance); + + REQUIRE(animationStateInstance->animationInstance()->time() == 1.0); + REQUIRE(animationStateInstance->animationInstance()->totalTime() == 11.0); + // Duration is 2s but at a -2x speed it takes 1s to end + // When advancing at negative speed, time starts at duration + // so starting at end and taking 1s to complete, it loops 5 times + // there is still 0.5s remaining (spilled) + REQUIRE(animationStateInstance->animationInstance()->spilledTime() == 0.5); + delete animationStateInstance; delete animationState; delete linearAnimation; diff --git a/test/assets/1.webp b/test/assets/1.webp new file mode 100644 index 00000000..122741b6 Binary files /dev/null and b/test/assets/1.webp differ diff --git a/test/assets/align_target.riv b/test/assets/align_target.riv new file mode 100644 index 00000000..38492393 Binary files /dev/null and b/test/assets/align_target.riv differ diff --git a/test/assets/animation_reset_cases.riv b/test/assets/animation_reset_cases.riv new file mode 100644 index 00000000..09e544f5 Binary files /dev/null and b/test/assets/animation_reset_cases.riv differ diff --git a/test/assets/bad.jpg b/test/assets/bad.jpg new file mode 100644 index 00000000..27a5d74a Binary files /dev/null and b/test/assets/bad.jpg differ diff --git a/test/assets/bad.png b/test/assets/bad.png new file mode 100644 index 00000000..5392a7ef Binary files /dev/null and b/test/assets/bad.png differ diff --git a/test/assets/bad_skin.riv b/test/assets/bad_skin.riv new file mode 100644 index 00000000..e5ba5d58 Binary files /dev/null and b/test/assets/bad_skin.riv differ diff --git a/test/assets/ball_test.riv b/test/assets/ball_test.riv new file mode 100644 index 00000000..e0e618da Binary files /dev/null and b/test/assets/ball_test.riv differ diff --git a/test/assets/click_event.riv b/test/assets/click_event.riv new file mode 100644 index 00000000..583f6b4e Binary files /dev/null and b/test/assets/click_event.riv differ diff --git a/test/assets/follow_path_path_0_opacity.riv b/test/assets/follow_path_path_0_opacity.riv new file mode 100644 index 00000000..ce4f0fba Binary files /dev/null and b/test/assets/follow_path_path_0_opacity.riv differ diff --git a/test/assets/layout/layout_center.riv b/test/assets/layout/layout_center.riv new file mode 100644 index 00000000..4b579617 Binary files /dev/null and b/test/assets/layout/layout_center.riv differ diff --git a/test/assets/layout/layout_horizontal.riv b/test/assets/layout/layout_horizontal.riv new file mode 100644 index 00000000..ad9e1b06 Binary files /dev/null and b/test/assets/layout/layout_horizontal.riv differ diff --git a/test/assets/layout/layout_horizontal_gaps.riv b/test/assets/layout/layout_horizontal_gaps.riv new file mode 100644 index 00000000..75def702 Binary files /dev/null and b/test/assets/layout/layout_horizontal_gaps.riv differ diff --git a/test/assets/layout/layout_horizontal_wrap.riv b/test/assets/layout/layout_horizontal_wrap.riv new file mode 100644 index 00000000..8ecfdbf6 Binary files /dev/null and b/test/assets/layout/layout_horizontal_wrap.riv differ diff --git a/test/assets/layout/layout_vertical.riv b/test/assets/layout/layout_vertical.riv new file mode 100644 index 00000000..742e001a Binary files /dev/null and b/test/assets/layout/layout_vertical.riv differ diff --git a/test/assets/layout/measure_tests.riv b/test/assets/layout/measure_tests.riv new file mode 100644 index 00000000..9d8595eb Binary files /dev/null and b/test/assets/layout/measure_tests.riv differ diff --git a/test/assets/nested_event_test.riv b/test/assets/nested_event_test.riv new file mode 100644 index 00000000..75c4cc08 Binary files /dev/null and b/test/assets/nested_event_test.riv differ diff --git a/test/assets/open_source.jpg b/test/assets/open_source.jpg new file mode 100644 index 00000000..710a4cf6 Binary files /dev/null and b/test/assets/open_source.jpg differ diff --git a/test/assets/placeholder.png b/test/assets/placeholder.png new file mode 100644 index 00000000..9c3668f8 Binary files /dev/null and b/test/assets/placeholder.png differ diff --git a/test/assets/pointer_events.riv b/test/assets/pointer_events.riv new file mode 100644 index 00000000..c65bea3c Binary files /dev/null and b/test/assets/pointer_events.riv differ diff --git a/test/assets/runtime_nested_inputs.riv b/test/assets/runtime_nested_inputs.riv new file mode 100644 index 00000000..04c2395c Binary files /dev/null and b/test/assets/runtime_nested_inputs.riv differ diff --git a/test/assets/runtime_nested_text_runs.riv b/test/assets/runtime_nested_text_runs.riv new file mode 100644 index 00000000..b7535a09 Binary files /dev/null and b/test/assets/runtime_nested_text_runs.riv differ diff --git a/test/assets/solar-system.riv b/test/assets/solar-system.riv new file mode 100644 index 00000000..acb0b980 Binary files /dev/null and b/test/assets/solar-system.riv differ diff --git a/test/assets/sound2.riv b/test/assets/sound2.riv new file mode 100644 index 00000000..4f7e9c56 Binary files /dev/null and b/test/assets/sound2.riv differ diff --git a/test/audio_test.cpp b/test/audio_test.cpp index f62e8446..734262ae 100644 --- a/test/audio_test.cpp +++ b/test/audio_test.cpp @@ -1,5 +1,6 @@ #include "rive/audio/audio_engine.hpp" #include "rive/audio/audio_source.hpp" +#include "rive/audio/audio_sound.hpp" #include "rive/audio/audio_reader.hpp" #include "rive/audio_event.hpp" #include "rive/assets/audio_asset.hpp" @@ -36,26 +37,43 @@ TEST_CASE("audio source can be opened", "[audio]") REQUIRE(engine != nullptr); auto file = loadFile("../../test/assets/audio/what.wav"); auto span = Span(file); - AudioSource audioSource(span); - REQUIRE(audioSource.channels() == 2); - REQUIRE(audioSource.sampleRate() == 44100); + rcp audioSource = rcp(new AudioSource(span)); + REQUIRE(audioSource->channels() == 2); + REQUIRE(audioSource->sampleRate() == 44100); // Try some different sample rates. { - auto reader = audioSource.makeReader(2, 44100); + auto reader = audioSource->makeReader(2, 44100); REQUIRE(reader != nullptr); REQUIRE(reader->lengthInFrames() == 9688); } { - auto reader = audioSource.makeReader(1, 48000); + auto reader = audioSource->makeReader(1, 48000); REQUIRE(reader != nullptr); REQUIRE(reader->lengthInFrames() == 10544); } { - auto reader = audioSource.makeReader(2, 32000); + auto reader = audioSource->makeReader(2, 32000); REQUIRE(reader != nullptr); REQUIRE(reader->lengthInFrames() == 7029); } + + float channels[2] = {0, 0}; + engine->initLevelMonitor(); + engine->levels(Span(&channels[0], 2)); + REQUIRE(channels[0] == 0); + REQUIRE(channels[1] == 0); + + auto sound = engine->play(audioSource, 0, 0, 0); + float frames[512 * 2] = {}; + engine->readAudioFrames(frames, 512); + engine->levels(Span(&channels[0], 2)); + REQUIRE(channels[0] != 0); + REQUIRE(channels[1] != 0); + + engine->readAudioFrames(frames, 512); + REQUIRE(engine->level(0) != 0); + REQUIRE(engine->level(1) != 0); } TEST_CASE("file with audio loads correctly", "[text]") @@ -83,4 +101,133 @@ TEST_CASE("file with audio loads correctly", "[text]") // artboard->advance(0.0f); // rive::NoOpRenderer renderer; // artboard->draw(&renderer); -} \ No newline at end of file +} + +TEST_CASE("audio sound can outlive engine", "[audio]") +{ + rcp sound; + { + rcp engine = AudioEngine::Make(2, 44100); + REQUIRE(engine != nullptr); + auto file = loadFile("../../test/assets/audio/what.wav"); + auto span = Span(file); + rcp audioSource = rcp(new AudioSource(span)); + REQUIRE(audioSource->channels() == 2); + REQUIRE(audioSource->sampleRate() == 44100); + + sound = engine->play(audioSource, 0, 0, 0); + float frames[512 * 2] = {}; + engine->readAudioFrames(frames, 512); + } + sound->stop(); +} + +TEST_CASE("many audio sounds can outlive engine", "[audio]") +{ + std::vector> sounds; + { + rcp engine = AudioEngine::Make(2, 44100); + REQUIRE(engine != nullptr); + auto file = loadFile("../../test/assets/audio/what.wav"); + auto span = Span(file); + rcp audioSource = rcp(new AudioSource(span)); + REQUIRE(audioSource->channels() == 2); + REQUIRE(audioSource->sampleRate() == 44100); + + for (int i = 0; i < 20; i++) + { + sounds.emplace_back(engine->play(audioSource, 0, 0, 0)); + } + float frames[512 * 2] = {}; + engine->readAudioFrames(frames, 512); + } + for (auto sound : sounds) + { + sound->stop(); + } +} + +TEST_CASE("audio sounds from different artboards stop accordingly", "[audio]") +{ + rcp engine = AudioEngine::Make(2, 44100); + + auto file = ReadRiveFile("../../test/assets/sound.riv"); + auto artboard = file->artboardDefault(); + artboard->audioEngine(engine); + auto artboard2 = file->artboardDefault(); + artboard2->audioEngine(engine); + + REQUIRE(artboard != nullptr); + + auto audioEvents = artboard->find(); + REQUIRE(audioEvents.size() == 1); + + auto audioEvent = audioEvents[0]; + REQUIRE(audioEvent->asset() != nullptr); + REQUIRE(audioEvent->asset()->hasAudioSource()); + + audioEvent->play(); + audioEvent->play(); + REQUIRE(engine->playingSoundCount() == 2); + auto audioEvent2 = artboard2->find()[0]; + audioEvent2->play(); + REQUIRE(engine->playingSoundCount() == 3); + audioEvent->play(); + REQUIRE(engine->playingSoundCount() == 4); + + // The three playing sounds owned by the first artboard should now stop. + artboard = nullptr; + + REQUIRE(engine->playingSoundCount() == 1); + + // The last one belonging to artboard2 should now stop too. + artboard2 = nullptr; + REQUIRE(engine->playingSoundCount() == 0); +} + +TEST_CASE("Artboard has audio", "[audio]") +{ + rcp engine = AudioEngine::Make(2, 44100); + + auto file = ReadRiveFile("../../test/assets/sound2.riv"); + auto artboard = file->artboardNamed("child"); + artboard->audioEngine(engine); + + REQUIRE(artboard != nullptr); + + auto audioEvents = artboard->find(); + REQUIRE(audioEvents.size() == 1); + REQUIRE(artboard->hasAudio() == true); +} + +TEST_CASE("Artboard has audio in nested artboard", "[audio]") +{ + rcp engine = AudioEngine::Make(2, 44100); + + auto file = ReadRiveFile("../../test/assets/sound2.riv"); + auto artboard = file->artboardNamed("grand-parent"); + artboard->audioEngine(engine); + + REQUIRE(artboard != nullptr); + + auto audioEvents = artboard->find(); + REQUIRE(audioEvents.size() == 0); + REQUIRE(artboard->hasAudio() == true); +} + +TEST_CASE("Artboard does not have audio", "[audio]") +{ + rcp engine = AudioEngine::Make(2, 44100); + + auto file = ReadRiveFile("../../test/assets/sound2.riv"); + auto artboard = file->artboardNamed("no-audio"); + artboard->audioEngine(engine); + + REQUIRE(artboard != nullptr); + + auto audioEvents = artboard->find(); + REQUIRE(audioEvents.size() == 0); + REQUIRE(artboard->hasAudio() == false); +} + +// TODO check if sound->stop calls completed callback!!! \ No newline at end of file diff --git a/test/clip_test.cpp b/test/clip_test.cpp index d31a3dd5..19ff1443 100644 --- a/test/clip_test.cpp +++ b/test/clip_test.cpp @@ -60,7 +60,7 @@ TEST_CASE("artboard is clipped correctly", "[clipping]") auto artboard = file->artboard("Center"); REQUIRE(artboard != nullptr); - artboard->updateComponents(); + artboard->advance(0.0f); REQUIRE(artboard->originX() == 0.5); REQUIRE(artboard->originY() == 0.5); { @@ -106,7 +106,7 @@ TEST_CASE("Shape does not have any clipping paths visible", "[clipping]") REQUIRE(clippedNode->is()); rive::Shape* clippedShape = static_cast(clippedNode); rive::NoOpRenderer renderer; - auto clipResult = clippedShape->clip(&renderer); + auto clipResult = clippedShape->applyClip(&renderer); REQUIRE(clipResult == rive::ClipResult::emptyClip); } @@ -128,7 +128,7 @@ TEST_CASE("Shape has at least a clipping path visible", "[clipping]") REQUIRE(clippedNode->is()); rive::Shape* clippedShape = static_cast(clippedNode); rive::NoOpRenderer renderer; - auto clipResult = clippedShape->clip(&renderer); + auto clipResult = clippedShape->applyClip(&renderer); REQUIRE(clipResult == rive::ClipResult::clip); } @@ -146,6 +146,6 @@ TEST_CASE("Shape returns an empty clip when one clipping shape is empty", "[clip rive::Shape* shape = static_cast(node); rive::NoOpRenderer renderer; - auto clipResult = shape->clip(&renderer); + auto clipResult = shape->applyClip(&renderer); REQUIRE(clipResult == rive::ClipResult::emptyClip); } diff --git a/test/contour_measure_test.cpp b/test/contour_measure_test.cpp index a52f1b6c..35483665 100644 --- a/test/contour_measure_test.cpp +++ b/test/contour_measure_test.cpp @@ -39,15 +39,15 @@ TEST_CASE("contour-basics", "[contourmeasure]") const float tol = 0.000001f; RawPath path; - ContourMeasureIter iter(path, false); + ContourMeasureIter iter(&path); REQUIRE(iter.next() == nullptr); path.moveTo(1, 2); - iter.rewind(path, false); + iter.rewind(&path); REQUIRE(iter.next() == nullptr); path.lineTo(4, 6); - iter.rewind(path, false); + iter.rewind(&path); auto cm = iter.next(); REQUIRE(cm); REQUIRE(nearly_eq(cm->length(), 5, tol)); @@ -58,7 +58,7 @@ TEST_CASE("contour-basics", "[contourmeasure]") path = RawPath(); const float w = 4, h = 6; path.addRect({0, 0, w, h}, PathDirection::cw); - iter.rewind(path, false); + iter.rewind(&path); cm = iter.next(); REQUIRE(cm); REQUIRE(nearly_eq(cm->length(), 2 * (w + h), tol)); @@ -118,7 +118,7 @@ TEST_CASE("multi-contours", "[contourmeasure]") path.addPoly(span, false); // len == 7 - ContourMeasureIter iter(path, false); + ContourMeasureIter iter(&path); auto cm = iter.next(); REQUIRE(cm->length() == 7); cm = iter.next(); @@ -136,7 +136,7 @@ TEST_CASE("contour-oval", "[contourmeasure]") const float r = 10; RawPath path; path.addOval({-r, -r, r, r}, PathDirection::cw); - ContourMeasureIter iter(path, false); + ContourMeasureIter iter(&path, tol); auto cm = iter.next(); REQUIRE(nearly_eq(cm->length(), 2 * r * math::PI, tol)); @@ -152,3 +152,50 @@ TEST_CASE("bad contour", "[contourmeasure]") auto machine = artboard->defaultStateMachine(); machine->advanceAndApply(0.0f); } + +// NaN paths don't return contours. +TEST_CASE("nan-path", "[contourmeasure]") +{ + RawPath path; + path.lineTo(1, 2); + path.cubicTo(3, 4, 5, 6, 7, 8); + path.cubicTo(9, 10, 11, 12, 13, 14); + path.cubicTo(15, 16, 17, 18, 19, 20); + + { + ContourMeasureIter iter(&path); + auto cm = iter.next(); + CHECK(cm != nullptr); + CHECK(std::isfinite(cm->length())); + CHECK(iter.next() == nullptr); + } + + { + auto nan = std::numeric_limits::quiet_NaN(); + RawPath path_ = path.transform(Mat2D(nan, nan, nan, nan, nan, nan)); + ContourMeasureIter iter(&path_); + CHECK(iter.next() == nullptr); + } +} + +// Regression test for a crash found by fuzzing. +TEST_CASE("fuzz_issue_7295", "[MetricsPath]") +{ + NoOpFactory factory; + + RawPath innerPath; + innerPath.moveTo(.0f, -20.5f); + innerPath.cubicTo(11.3218384f, -20.5f, 20.5f, -11.3218384f, 20.5f, .0f); + innerPath.cubicTo(20.5f, 11.3218384f, 11.3218384f, 20.5f, .0f, 20.5f); + innerPath.cubicTo(-11.3218384f, 20.5f, -20.5f, 11.3218384f, -20.5f, .0f); + innerPath.cubicTo(-20.5f, -11.3218384f, -11.3218384f, -20.5f, .0f, -20.5f); + + RawPath outerPath; + Mat2D transform(1.f, .0f, .0f, 1.f, -134217728.f, -134217728.f); + outerPath.addPath(innerPath, &transform); + + auto contour = ContourMeasureIter(&outerPath).next(); + RawPath result; + contour->getSegment(.0f, 168.389008f, &result, true); + CHECK(math::nearly_equal(contour->length(), 168.389008f)); +} diff --git a/test/file_test.cpp b/test/file_test.cpp index 504014c6..69ec0422 100644 --- a/test/file_test.cpp +++ b/test/file_test.cpp @@ -1,8 +1,10 @@ -#include -#include -#include -#include -#include +#include "rive/file.hpp" +#include "rive/node.hpp" +#include "rive/shapes/rectangle.hpp" +#include "rive/shapes/shape.hpp" +#include "rive/assets/image_asset.hpp" +#include "rive/shapes/points_path.hpp" +#include "rive/shapes/mesh.hpp" #include "utils/no_op_renderer.hpp" #include "rive_file_reader.hpp" #include @@ -38,6 +40,15 @@ TEST_CASE("file can be read", "[file]") REQUIRE(file->artboard("One") != nullptr); } +TEST_CASE("file with bad blend mode fails to load", "[file]") +{ + std::vector bytes = ReadFile("../../test/assets/solar-system.riv"); + + rive::ImportResult result; + auto file = rive::File::import(bytes, &gNoOpFactory, &result, nullptr); + CHECK(result == rive::ImportResult::malformed); +} + TEST_CASE("file with animation can be read", "[file]") { auto file = ReadRiveFile("../../test/assets/juice.riv"); @@ -184,6 +195,35 @@ TEST_CASE("file with in-band images can have the stripped", "[file]") } } +TEST_CASE("file a bad skin (no parent skinnable) doesn't crash", "[file]") +{ + FILE* fp = fopen("../../test/assets/bad_skin.riv", "rb"); + REQUIRE(fp != nullptr); + + fseek(fp, 0, SEEK_END); + const size_t length = ftell(fp); + fseek(fp, 0, SEEK_SET); + std::vector bytes(length); + REQUIRE(fread(bytes.data(), 1, length, fp) == length); + fclose(fp); + + rive::ImportResult result; + auto file = rive::File::import(bytes, &gNoOpFactory, &result); + REQUIRE(result == rive::ImportResult::success); + REQUIRE(file.get() != nullptr); + REQUIRE(file->artboard() != nullptr); + + REQUIRE(file->artboard()->name() == "Illustration WOman.svg"); + auto artboard = file->artboardDefault(); + artboard->updateComponents(); + auto paths = artboard->find(); + for (auto path : paths) + { + path->markPathDirty(); + } + artboard->updateComponents(); +} + // TODO: // ShapePaint (fill/stroke) needs to be implemented in WASM (jsFill/jsStroke) in // order to create Paint objects as necessary. diff --git a/test/follow_path_constraint_test.cpp b/test/follow_path_constraint_test.cpp index 20901867..faff9ec8 100644 --- a/test/follow_path_constraint_test.cpp +++ b/test/follow_path_constraint_test.cpp @@ -46,3 +46,23 @@ TEST_CASE("follow path with 0 opacity constraint updates world transform", "[fil REQUIRE(targetComponents.x() == rectComponents.x()); REQUIRE(targetComponents.y() == rectComponents.y()); } + +TEST_CASE("follow path constraint with path at 0 opacity updates world transform", "[file]") +{ + auto file = ReadRiveFile("../../test/assets/follow_path_path_0_opacity.riv"); + + auto artboard = file->artboard(); + + REQUIRE(artboard->find("target") != nullptr); + auto target = artboard->find("target"); + + REQUIRE(artboard->find("rect") != nullptr); + auto rectangle = artboard->find("rect"); + + artboard->advance(0.0f); + + auto targetComponents = target->worldTransform().decompose(); + auto rectComponents = rectangle->worldTransform().decompose(); + REQUIRE(targetComponents.x() == rectComponents.x()); + REQUIRE(targetComponents.y() == rectComponents.y()); +} diff --git a/test/hittest_test.cpp b/test/hittest_test.cpp index cb4f8f8c..6c12c9f8 100644 --- a/test/hittest_test.cpp +++ b/test/hittest_test.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "rive_file_reader.hpp" #include @@ -161,6 +162,11 @@ TEST_CASE("hit test on opaque nested artboard", "[hittest]") // toggle changes value because it is not under an opaque nested artboard REQUIRE(secondGrayToggle->value() == true); + stateMachineInstance->pointerDown(rive::Vec2D(301.0f, 50.0f)); + // toggle does not change because it is beyond the area of the square by 1 pixel + // And the 2px padding is unly used after the coarse grained test passes + REQUIRE(secondGrayToggle->value() == true); + stateMachineInstance->pointerDown(rive::Vec2D(100.0f, 50.0f)); // toggle does not change because it is under an opaque nested artboard REQUIRE(secondGrayToggle->value() == true); @@ -178,5 +184,203 @@ TEST_CASE("hit test on opaque nested artboard", "[hittest]") // nested toggle does not change because it's below shape REQUIRE(secondNestedBoolTarget->value() == true); + delete stateMachineInstance; +} + +TEST_CASE("early out on listeners", "[hittest]") +{ + auto file = ReadRiveFile("../../test/assets/pointer_events.riv"); + + auto artboard = file->artboard("art-1"); + auto artboardInstance = artboard->instance(); + auto stateMachine = artboard->stateMachine("sm-1"); + + REQUIRE(artboardInstance != nullptr); + REQUIRE(artboardInstance->stateMachineCount() == 1); + + REQUIRE(stateMachine != nullptr); + + rive::StateMachineInstance* stateMachineInstance = + new rive::StateMachineInstance(stateMachine, artboardInstance.get()); + + stateMachineInstance->advance(0.0f); + artboardInstance->advance(0.0f); + REQUIRE(stateMachineInstance->needsAdvance() == true); + stateMachineInstance->advance(0.0f); + REQUIRE(stateMachineInstance->hitComponentsCount() == 4); + // Hit component with only pointer down and pointer up listeners + auto hitComponentWithEarlyOut = stateMachineInstance->hitComponent(0); + // Hit component that can't early out because it has a pointer enter event + auto hitComponentWithNoEarlyOut = stateMachineInstance->hitComponent(1); + // Hit component that can't early out because it is an opaque target + auto hitComponentOpaque = stateMachineInstance->hitComponent(2); + // Hit component that can early out on all and pointer up + auto hitComponentOnlyPointerDown = stateMachineInstance->hitComponent(3); + REQUIRE(hitComponentWithEarlyOut->earlyOutCount == 0); + REQUIRE(hitComponentWithNoEarlyOut->earlyOutCount == 0); + REQUIRE(hitComponentOpaque->earlyOutCount == 0); + REQUIRE(hitComponentOnlyPointerDown->earlyOutCount == 0); + stateMachineInstance->pointerMove(rive::Vec2D(100.0f, 250.0f)); + REQUIRE(hitComponentWithEarlyOut->earlyOutCount == 1); + REQUIRE(hitComponentWithNoEarlyOut->earlyOutCount == 0); + REQUIRE(hitComponentOpaque->earlyOutCount == 0); + REQUIRE(hitComponentOnlyPointerDown->earlyOutCount == 1); + stateMachineInstance->pointerExit(rive::Vec2D(100.0f, 250.0f)); + REQUIRE(hitComponentWithEarlyOut->earlyOutCount == 2); + REQUIRE(hitComponentWithNoEarlyOut->earlyOutCount == 0); + REQUIRE(hitComponentOnlyPointerDown->earlyOutCount == 2); + REQUIRE(hitComponentOpaque->earlyOutCount == 0); + stateMachineInstance->pointerDown(rive::Vec2D(100.0f, 250.0f)); + REQUIRE(hitComponentWithEarlyOut->earlyOutCount == 2); + REQUIRE(hitComponentWithNoEarlyOut->earlyOutCount == 0); + REQUIRE(hitComponentOpaque->earlyOutCount == 0); + REQUIRE(hitComponentOnlyPointerDown->earlyOutCount == 2); + stateMachineInstance->pointerUp(rive::Vec2D(100.0f, 250.0f)); + REQUIRE(hitComponentWithEarlyOut->earlyOutCount == 2); + REQUIRE(hitComponentWithNoEarlyOut->earlyOutCount == 0); + REQUIRE(hitComponentOpaque->earlyOutCount == 0); + REQUIRE(hitComponentOnlyPointerDown->earlyOutCount == 3); + stateMachineInstance->pointerMove(rive::Vec2D(105.0f, 205.0f)); + REQUIRE(hitComponentWithEarlyOut->earlyOutCount == 3); + REQUIRE(hitComponentWithNoEarlyOut->earlyOutCount == 0); + REQUIRE(hitComponentOpaque->earlyOutCount == 0); + REQUIRE(hitComponentOnlyPointerDown->earlyOutCount == 4); + + delete stateMachineInstance; +} + +TEST_CASE("click event", "[hittest]") +{ + // This test has two rectangles of size [200, 200] + // positioned at [100,100] and [200, 200] + // they overlap between coordinates [100,100]-[200, 200] + // they are inside a group that has a listener attached to it + // that listener should fire an event on "Click" + auto file = ReadRiveFile("../../test/assets/click_event.riv"); + + auto artboard = file->artboard("art-1"); + auto artboardInstance = artboard->instance(); + auto stateMachine = artboard->stateMachine("sm-1"); + + REQUIRE(artboardInstance != nullptr); + REQUIRE(artboardInstance->stateMachineCount() == 1); + + REQUIRE(stateMachine != nullptr); + + rive::StateMachineInstance* stateMachineInstance = + new rive::StateMachineInstance(stateMachine, artboardInstance.get()); + + stateMachineInstance->advance(0.0f); + artboardInstance->advance(0.0f); + REQUIRE(stateMachineInstance->needsAdvance() == true); + stateMachineInstance->advance(0.0f); + // There is a single listener with two shapes in it + REQUIRE(stateMachineInstance->hitComponentsCount() == 2); + auto layerCount = stateMachine->layerCount(); + REQUIRE(layerCount == 1); + REQUIRE(stateMachineInstance->reportedEventCount() == 0); + // Click in place should trigger a click event + stateMachineInstance->pointerDown(rive::Vec2D(75.0f, 75.0f)); + stateMachineInstance->pointerUp(rive::Vec2D(75.0f, 75.0f)); + REQUIRE(stateMachineInstance->reportedEventCount() == 1); + // Pointer down inside shape but Pointer up outside the shape + // should not trigger a click event + stateMachineInstance->pointerDown(rive::Vec2D(75.0f, 75.0f)); + stateMachineInstance->pointerUp(rive::Vec2D(300.0f, 75.0f)); + REQUIRE(stateMachineInstance->reportedEventCount() == 1); + // Pointer down outside shape but Pointer up inside the shape + // should not trigger a click event + stateMachineInstance->pointerDown(rive::Vec2D(300.0f, 75.0f)); + stateMachineInstance->pointerUp(rive::Vec2D(75.0f, 75.0f)); + REQUIRE(stateMachineInstance->reportedEventCount() == 1); + // Pointer down in shape 1 Pointer up in shape 2 of the same group + // should trigger a click event + stateMachineInstance->pointerDown(rive::Vec2D(75.0f, 75.0f)); + stateMachineInstance->pointerUp(rive::Vec2D(225.0f, 225.0f)); + REQUIRE(stateMachineInstance->reportedEventCount() == 2); + // Pointer down and up in area where both shapes overlap + // should trigger a single click event + stateMachineInstance->pointerDown(rive::Vec2D(150.0f, 150.0f)); + stateMachineInstance->pointerUp(rive::Vec2D(150.0f, 150.0f)); + REQUIRE(stateMachineInstance->reportedEventCount() == 3); + + delete stateMachineInstance; +} + +TEST_CASE("multiple shapes with mouse movement behavior", "[hittest]") +{ + // This test has two rectangles of size [200, 200] + // positioned at [100,100] and [100, 200] + // they overlap between coordinates [100,0]-[200, 200] + // they are inside a group that has a Pointer enter and a Pointer out + // listeners that toggle between two states (red and green) + // starting at "red" + auto file = ReadRiveFile("../../test/assets/click_event.riv"); + + auto artboard = file->artboard("art-2"); + auto artboardInstance = artboard->instance(); + auto stateMachine = artboard->stateMachine("sm-1"); + + REQUIRE(artboardInstance != nullptr); + REQUIRE(artboardInstance->stateMachineCount() == 1); + + REQUIRE(stateMachine != nullptr); + + rive::StateMachineInstance* stateMachineInstance = + new rive::StateMachineInstance(stateMachine, artboardInstance.get()); + + stateMachineInstance->advance(0.0f); + artboardInstance->advance(0.0f); + REQUIRE(stateMachineInstance->needsAdvance() == true); + stateMachineInstance->advance(0.0f); + // There is a single listener with two shapes in it + REQUIRE(stateMachineInstance->hitComponentsCount() == 2); + auto layerCount = stateMachine->layerCount(); + REQUIRE(layerCount == 1); + // Move over the first shape + stateMachineInstance->pointerMove(rive::Vec2D(75.0f, 75.0f)); + artboardInstance->advance(0.0f); + stateMachineInstance->advanceAndApply(0.0f); + + { + auto state = stateMachineInstance->layerState(0); + REQUIRE(state->is()); + auto animation = state->as()->animation(); + REQUIRE(animation->name() == "green"); + } + // Move over the second shape, nothing should change + stateMachineInstance->pointerMove(rive::Vec2D(200.0f, 75.0f)); + artboardInstance->advance(0.0f); + stateMachineInstance->advanceAndApply(0.0f); + + { + auto state = stateMachineInstance->layerState(0); + REQUIRE(state->is()); + auto animation = state->as()->animation(); + REQUIRE(animation->name() == "green"); + } + // Move out of the second shape, should go back to red + stateMachineInstance->pointerMove(rive::Vec2D(400.0f, 75.0f)); + artboardInstance->advance(0.0f); + stateMachineInstance->advanceAndApply(0.0f); + + { + auto state = stateMachineInstance->layerState(0); + REQUIRE(state->is()); + auto animation = state->as()->animation(); + REQUIRE(animation->name() == "red"); + } + // Move back into the second shape, should go to green + stateMachineInstance->pointerMove(rive::Vec2D(200.0f, 75.0f)); + artboardInstance->advance(0.0f); + stateMachineInstance->advanceAndApply(0.0f); + + { + auto state = stateMachineInstance->layerState(0); + REQUIRE(state->is()); + auto animation = state->as()->animation(); + REQUIRE(animation->name() == "green"); + } + delete stateMachineInstance; } \ No newline at end of file diff --git a/test/image_decoders_test.cpp b/test/image_decoders_test.cpp new file mode 100644 index 00000000..024115b8 --- /dev/null +++ b/test/image_decoders_test.cpp @@ -0,0 +1,79 @@ +#include "rive_file_reader.hpp" +#include "rive_testing.hpp" +#include "rive/decoders/bitmap_decoder.hpp" + +TEST_CASE("png file decodes correctly", "[image-decoder]") +{ + auto file = ReadFile("../../test/assets/placeholder.png"); + REQUIRE(file.size() == 1096); + + auto bitmap = Bitmap::decode(file.data(), file.size()); + + REQUIRE(bitmap != nullptr); + + REQUIRE(bitmap->width() == 226); + REQUIRE(bitmap->height() == 128); +} + +TEST_CASE("jpeg file decodes correctly", "[image-decoder]") +{ + auto file = ReadFile("../../test/assets/open_source.jpg"); + REQUIRE(file.size() == 8880); + + auto bitmap = Bitmap::decode(file.data(), file.size()); + + REQUIRE(bitmap != nullptr); + + REQUIRE(bitmap->width() == 350); + REQUIRE(bitmap->height() == 200); +} + +#ifndef __APPLE__ +// Loading this particular jpeg image in CG causes a memory leak CGImageSourceCreateImageAtIndex +// calls IIOReadPlugin::createInfoPtr which leaks +TEST_CASE("bad jpeg file doesn't cause an overflow", "[image-decoder]") +{ + auto file = ReadFile("../../test/assets/bad.jpg"); + REQUIRE(file.size() == 88731); + + auto bitmap = Bitmap::decode(file.data(), file.size()); + + REQUIRE(bitmap != nullptr); + + REQUIRE(bitmap->width() == 24566); + REQUIRE(bitmap->height() == 58278); +} +#endif + +TEST_CASE("bad png file doesn't cause an overflow", "[image-decoder]") +{ + auto file = ReadFile("../../test/assets/bad.png"); + REQUIRE(file.size() == 534283); + + auto bitmap = Bitmap::decode(file.data(), file.size()); + +#ifdef __APPLE__ + // Loading this bad PNG file in CG actually works and we do get an image albiet black + REQUIRE(bitmap != nullptr); + + REQUIRE(bitmap->width() == 58278); + REQUIRE(bitmap->height() == 24566); +#else + // Our decoders return null as we have an invalid header with bogus resolution and we want to + // avoid a potential attack vector + REQUIRE(bitmap == nullptr); +#endif +} + +TEST_CASE("webp file decodes correctly", "[image-decoder]") +{ + auto file = ReadFile("../../test/assets/1.webp"); + REQUIRE(file.size() == 30320); + + auto bitmap = Bitmap::decode(file.data(), file.size()); + + REQUIRE(bitmap != nullptr); + + REQUIRE(bitmap->width() == 550); + REQUIRE(bitmap->height() == 368); +} \ No newline at end of file diff --git a/test/layout_test.cpp b/test/layout_test.cpp new file mode 100644 index 00000000..e7176a13 --- /dev/null +++ b/test/layout_test.cpp @@ -0,0 +1,143 @@ +#include "rive/math/transform_components.hpp" +#include "rive/shapes/rectangle.hpp" +#include "rive/text/text.hpp" +#include "utils/no_op_factory.hpp" +#include "rive_file_reader.hpp" +#include "rive_testing.hpp" +#include +#include + +TEST_CASE("LayoutComponent FlexDirection row", "[layout]") +{ + auto file = ReadRiveFile("../../test/assets/layout/layout_horizontal.riv"); + + auto artboard = file->artboard(); + + REQUIRE(artboard->find("LayoutComponent1") != nullptr); + auto target1 = artboard->find("LayoutComponent1"); + + REQUIRE(artboard->find("LayoutComponent2") != nullptr); + auto target2 = artboard->find("LayoutComponent2"); + + REQUIRE(artboard->find("LayoutComponent3") != nullptr); + auto target3 = artboard->find("LayoutComponent3"); + + artboard->advance(0.0f); + auto target1Components = target1->worldTransform().decompose(); + auto target2Components = target2->worldTransform().decompose(); + auto target3Components = target3->worldTransform().decompose(); + + REQUIRE(target1Components.x() == 0); + REQUIRE(target2Components.x() == 100); + REQUIRE(target3Components.x() == 200); + REQUIRE(target1Components.y() == 0); + REQUIRE(target2Components.y() == 0); + REQUIRE(target3Components.y() == 0); +} + +TEST_CASE("LayoutComponent FlexDirection column", "[layout]") +{ + auto file = ReadRiveFile("../../test/assets/layout/layout_vertical.riv"); + + auto artboard = file->artboard(); + + REQUIRE(artboard->find("LayoutComponent1") != nullptr); + auto target1 = artboard->find("LayoutComponent1"); + + REQUIRE(artboard->find("LayoutComponent2") != nullptr); + auto target2 = artboard->find("LayoutComponent2"); + + REQUIRE(artboard->find("LayoutComponent3") != nullptr); + auto target3 = artboard->find("LayoutComponent3"); + + artboard->advance(0.0f); + auto target1Components = target1->worldTransform().decompose(); + auto target2Components = target2->worldTransform().decompose(); + auto target3Components = target3->worldTransform().decompose(); + + REQUIRE(target1Components.x() == 0); + REQUIRE(target2Components.x() == 0); + REQUIRE(target3Components.x() == 0); + REQUIRE(target1Components.y() == 0); + REQUIRE(target2Components.y() == 100); + REQUIRE(target3Components.y() == 200); +} + +TEST_CASE("LayoutComponent FlexDirection row with gap", "[layout]") +{ + auto file = ReadRiveFile("../../test/assets/layout/layout_horizontal_gaps.riv"); + + auto artboard = file->artboard(); + + REQUIRE(artboard->find("LayoutComponent1") != nullptr); + auto target1 = artboard->find("LayoutComponent1"); + + REQUIRE(artboard->find("LayoutComponent2") != nullptr); + auto target2 = artboard->find("LayoutComponent2"); + + REQUIRE(artboard->find("LayoutComponent3") != nullptr); + auto target3 = artboard->find("LayoutComponent3"); + + artboard->advance(0.0f); + auto target1Components = target1->worldTransform().decompose(); + auto target2Components = target2->worldTransform().decompose(); + auto target3Components = target3->worldTransform().decompose(); + + REQUIRE(target1Components.x() == 0); + REQUIRE(target2Components.x() == 110); + REQUIRE(target3Components.x() == 220); + REQUIRE(target1Components.y() == 0); + REQUIRE(target2Components.y() == 0); + REQUIRE(target3Components.y() == 0); +} + +TEST_CASE("LayoutComponent FlexDirection row with wrap", "[layout]") +{ + auto file = ReadRiveFile("../../test/assets/layout/layout_horizontal_wrap.riv"); + + auto artboard = file->artboard(); + + REQUIRE(artboard->find("LayoutComponent6") != nullptr); + auto target = artboard->find("LayoutComponent6"); + + artboard->advance(0.0f); + auto targetComponents = target->worldTransform().decompose(); + + REQUIRE(targetComponents.x() == 0); + REQUIRE(targetComponents.y() == 100); +} + +TEST_CASE("LayoutComponent Center using alignItems and justifyContent", "[layout]") +{ + auto file = ReadRiveFile("../../test/assets/layout/layout_center.riv"); + + auto artboard = file->artboard(); + + REQUIRE(artboard->find("LayoutComponent1") != nullptr); + auto target = artboard->find("LayoutComponent1"); + + artboard->advance(0.0f); + auto targetComponents = target->worldTransform().decompose(); + + REQUIRE(targetComponents.x() == 200); + REQUIRE(targetComponents.y() == 200); +} + +TEST_CASE("LayoutComponent with intrinsic size gets measured correctly", "[layout]") +{ + auto file = ReadRiveFile("../../test/assets/layout/measure_tests.riv"); + + auto artboard = file->artboard("hi"); + + REQUIRE(artboard->find("TextLayout") != nullptr); + REQUIRE(artboard->find("HiText") != nullptr); + + artboard->advance(0.0f); + + auto text = artboard->find("HiText"); + auto bounds = text->localBounds(); + REQUIRE(bounds.left() == 0); + REQUIRE(bounds.top() == 0); + REQUIRE(bounds.width() == 62.48047f); + REQUIRE(bounds.height() == 72.62695f); +} diff --git a/test/listener_align_target_test.cpp b/test/listener_align_target_test.cpp new file mode 100644 index 00000000..f41dbab0 --- /dev/null +++ b/test/listener_align_target_test.cpp @@ -0,0 +1,82 @@ +/* + * Copyright 2022 Rive + */ + +#include +#include +#include +#include +#include +#include +#include "rive_file_reader.hpp" + +#include +#include + +using namespace rive; + +TEST_CASE("align target with preserve offset off test", "[listener_align]") +{ + // The circle starts at coords 100, 100 + // Once the pointer move has acted, the new coords should be 100, 51 + auto file = ReadRiveFile("../../test/assets/align_target.riv"); + + auto artboard = file->artboard("preserve-inactive"); + auto artboardInstance = artboard->instance(); + auto stateMachine = artboard->stateMachine("align-state-machine"); + + REQUIRE(artboardInstance != nullptr); + REQUIRE(artboardInstance->stateMachineCount() == 1); + + REQUIRE(stateMachine != nullptr); + + rive::StateMachineInstance* stateMachineInstance = + new rive::StateMachineInstance(stateMachine, artboardInstance.get()); + + artboardInstance->advance(0.0f); + stateMachineInstance->advanceAndApply(0.0f); + REQUIRE(stateMachineInstance->needsAdvance() == true); + stateMachineInstance->advance(0.0f); + auto circle = stateMachineInstance->artboard()->find("circle"); + REQUIRE(circle != nullptr); + stateMachineInstance->pointerMove(rive::Vec2D(100.0f, 50.0f)); + stateMachineInstance->pointerMove(rive::Vec2D(100.0f, 51.0f)); + stateMachineInstance->advanceAndApply(1.0f); + stateMachineInstance->advance(0.0f); + REQUIRE(circle->x() == 100.0f); + REQUIRE(circle->y() == 51.0f); + delete stateMachineInstance; +} + +TEST_CASE("align target preserve offset test", "[listener_align]") +{ + // The circle starts at coords 100, 100 + // Once the pointer move has acted, the new coords should be 100, 101 + auto file = ReadRiveFile("../../test/assets/align_target.riv"); + + auto artboard = file->artboard("preserve-active"); + auto artboardInstance = artboard->instance(); + auto stateMachine = artboard->stateMachine("align-state-machine"); + + REQUIRE(artboardInstance != nullptr); + REQUIRE(artboardInstance->stateMachineCount() == 1); + + REQUIRE(stateMachine != nullptr); + + rive::StateMachineInstance* stateMachineInstance = + new rive::StateMachineInstance(stateMachine, artboardInstance.get()); + + artboardInstance->advance(0.0f); + stateMachineInstance->advanceAndApply(0.0f); + REQUIRE(stateMachineInstance->needsAdvance() == true); + stateMachineInstance->advance(0.0f); + auto circle = stateMachineInstance->artboard()->find("circle"); + REQUIRE(circle != nullptr); + stateMachineInstance->pointerMove(rive::Vec2D(100.0f, 50.0f)); + stateMachineInstance->pointerMove(rive::Vec2D(100.0f, 51.0f)); + stateMachineInstance->advanceAndApply(1.0f); + stateMachineInstance->advance(0.0f); + REQUIRE(circle->x() == 100.0f); + REQUIRE(circle->y() == 101.0f); + delete stateMachineInstance; +} \ No newline at end of file diff --git a/test/mat2d_test.cpp b/test/mat2d_test.cpp index aa904ead..cc807131 100644 --- a/test/mat2d_test.cpp +++ b/test/mat2d_test.cpp @@ -233,5 +233,26 @@ TEST_CASE("mapBoundingBox", "[Mat2D]") checkMatrix(Mat2D(-12, -13, -14, -15, -16, -17)); checkMatrix(Mat2D(18, 19, 20, 21, 22, 23)); checkMatrix(Mat2D(-25, 26, 27, -28, 29, -30)); + + // Mapping empty or NaN points returns 0. + CHECK(Mat2D().mapBoundingBox(nullptr, 0) == AABB()); + auto nan = std::numeric_limits::quiet_NaN(); + CHECK(Mat2D().mapBoundingBox(AABB{nan, nan, nan, nan}) == AABB()); + + // NaN values are otherwise ignored. + CHECK(Mat2D().mapBoundingBox(AABB{-1, -1, 1, 1}) == AABB{-1, -1, 1, 1}); + CHECK(Mat2D().mapBoundingBox(AABB{nan, -1, 1, 1}) == AABB{1, -1, 1, 1}); + CHECK(Mat2D().mapBoundingBox(AABB{-1, nan, 1, 1}) == AABB{-1, 1, 1, 1}); + CHECK(Mat2D().mapBoundingBox(AABB{-1, -1, nan, 1}) == AABB{-1, -1, -1, 1}); + CHECK(Mat2D().mapBoundingBox(AABB{-1, -1, 1, nan}) == AABB{-1, -1, 1, -1}); + + // When AABB::height() inf - inf, the result is nan. + auto inf = std::numeric_limits::infinity(); + CHECK(Mat2D().mapBoundingBox(AABB{0, inf, 0, nan}).height() == 0); + CHECK(Mat2D().mapBoundingBox(AABB{0, -inf, 0, nan}).height() == 0); + CHECK(Mat2D().mapBoundingBox(AABB{inf, 0, nan, 0}).width() == 0); + CHECK(Mat2D().mapBoundingBox(AABB{-inf, 0, nan, 0}).width() == 0); + CHECK(Mat2D().mapBoundingBox(AABB{inf, 0, inf, 0}).width() == 0); + CHECK(Mat2D().mapBoundingBox(AABB{0, -inf, 0, -inf}).height() == 0); } } // namespace rive diff --git a/test/metrics_path_test.cpp b/test/metrics_path_test.cpp deleted file mode 100644 index 44efab79..00000000 --- a/test/metrics_path_test.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include - -TEST_CASE("path metrics compute correctly", "[bezier]") -{ - // TODO: fix these based on new logic - // Make a square with sides length 10. - // rive::OnlyMetricsPath path; - // path.moveTo(0, 0); - // path.lineTo(10, 0); - // path.lineTo(10, 10); - // path.lineTo(0, 10); - // path.close(); - - // // Total length should be 40. - // rive::Mat2D identity; - // float length = path.computeLength(identity); - // REQUIRE(length == 40); - - // // Make a path with a single cubic. - // rive::OnlyMetricsPath cubicPath; - // cubicPath.moveTo(102, 22); - // cubicPath.cubicTo(10, 80, 120, 100, 150, 222); - // cubicPath.close(); - - // float cubicLength = cubicPath.computeLength(identity); - // REQUIRE(cubicLength == 238.38698f); -} \ No newline at end of file diff --git a/test/nested_artboard.cpp b/test/nested_artboard.cpp index bac597a7..fa886261 100644 --- a/test/nested_artboard.cpp +++ b/test/nested_artboard.cpp @@ -27,14 +27,40 @@ TEST_CASE("collapsed nested artboards do not advance", "[solo]") // if checking whether the time of each artboard has advanced // Unfortunately there is no way of accessing the time of the animations directly auto redNestedArtboard = stateMachine->artboard()->find("red-artboard"); - auto redNestedArtboardArtboard = redNestedArtboard->artboard(); + auto redNestedArtboardArtboard = redNestedArtboard->artboardInstance(); auto movingShapes = redNestedArtboardArtboard->find(); auto redRect = movingShapes.at(0); REQUIRE(redRect->x() > 50); auto greenNestedArtboard = stateMachine->artboard()->find("green-artboard"); - auto greenNestedArtboardArtboard = greenNestedArtboard->artboard(); + auto greenNestedArtboardArtboard = greenNestedArtboard->artboardInstance(); auto greenMovingShapes = greenNestedArtboardArtboard->find(); auto greenRect = greenMovingShapes.at(0); REQUIRE(greenRect->x() == 50); +} + +TEST_CASE("nested artboards with looping animations will keep main advanceAndApply advancing", + "[nested]") +{ + auto file = ReadRiveFile("../../test/assets/ball_test.riv"); + auto artboard = file->artboard("Artboard")->instance(); + artboard->advance(0.0f); + auto stateMachine = artboard->stateMachineAt(0); + REQUIRE(stateMachine->advanceAndApply(0.0f) == true); + REQUIRE(stateMachine->advanceAndApply(1.0f) == true); + REQUIRE(stateMachine->advanceAndApply(1.0f) == true); +} +TEST_CASE("nested artboards with one shot animations will not main advanceAndApply advancing", + "[nested]") +{ + + auto file = ReadRiveFile("../../test/assets/ball_test.riv"); + auto artboard = file->artboard("Artboard 2")->instance(); + artboard->advance(0.0f); + auto stateMachine = artboard->stateMachineAt(0); + REQUIRE(stateMachine->advanceAndApply(0.0f) == true); + REQUIRE(stateMachine->advanceAndApply(0.9f) == true); + REQUIRE(stateMachine->advanceAndApply(0.1f) == true); + // nested artboards animation is 1s long + REQUIRE(stateMachine->advanceAndApply(0.1f) == false); } \ No newline at end of file diff --git a/test/nested_artboard_opacity_test.cpp b/test/nested_artboard_opacity_test.cpp index 5fdd19e5..209f2092 100644 --- a/test/nested_artboard_opacity_test.cpp +++ b/test/nested_artboard_opacity_test.cpp @@ -16,8 +16,8 @@ TEST_CASE("Nested artboard background renders with opacity", "[file]") REQUIRE(artboard->find("Nested artboard container") != nullptr); auto nestedArtboardContainer = artboard->find("Nested artboard container"); - REQUIRE(nestedArtboardContainer->artboard() != nullptr); - auto nestedArtboard = nestedArtboardContainer->artboard(); + REQUIRE(nestedArtboardContainer->artboardInstance() != nullptr); + auto nestedArtboard = nestedArtboardContainer->artboardInstance(); nestedArtboard->updateComponents(); auto paints = nestedArtboard->shapePaints(); REQUIRE(paints.size() == 1); diff --git a/test/nested_input_test.cpp b/test/nested_input_test.cpp new file mode 100644 index 00000000..a40b58c6 --- /dev/null +++ b/test/nested_input_test.cpp @@ -0,0 +1,139 @@ +#include "rive/core/binary_reader.hpp" +#include "rive/file.hpp" +#include "rive/nested_artboard.hpp" +#include "rive/animation/nested_bool.hpp" +#include "rive/animation/nested_input.hpp" +#include "rive/animation/nested_number.hpp" +#include "rive/animation/nested_trigger.hpp" +#include "rive/animation/state_machine_instance.hpp" +#include "rive/animation/state_machine_input_instance.hpp" +#include "catch.hpp" +#include "rive_file_reader.hpp" +#include + +TEST_CASE("validate nested boolean get/set", "[nestedInput]") +{ + auto file = ReadRiveFile("../../test/assets/runtime_nested_inputs.riv"); + + auto artboard = file->artboard("MainArtboard")->instance(); + REQUIRE(artboard != nullptr); + REQUIRE(artboard->stateMachineCount() == 1); + + // Test getting/setting boolean SMIInput via nested artboard path + auto boolInput = artboard->getBool("CircleOuterState", "CircleOuter"); + auto smiInput = artboard->input("CircleOuterState", "CircleOuter"); + auto smiBoolInput = static_cast(smiInput); + auto nestedArtboard = artboard->nestedArtboard("CircleOuter"); + auto nestedInput = nestedArtboard->input("CircleOuterState")->as(); + REQUIRE(boolInput->value() == false); + REQUIRE(smiBoolInput->value() == false); + REQUIRE(nestedInput->nestedValue() == false); + + boolInput->value(true); + REQUIRE(boolInput->value() == true); + REQUIRE(smiBoolInput->value() == true); + REQUIRE(nestedInput->nestedValue() == true); + + smiBoolInput->value(false); + REQUIRE(boolInput->value() == false); + REQUIRE(smiBoolInput->value() == false); + REQUIRE(nestedInput->nestedValue() == false); + + nestedInput->nestedValue(true); + REQUIRE(boolInput->value() == true); + REQUIRE(smiBoolInput->value() == true); + REQUIRE(nestedInput->nestedValue() == true); +} + +TEST_CASE("validate nested number get/set", "[nestedInput]") +{ + auto file = ReadRiveFile("../../test/assets/runtime_nested_inputs.riv"); + + auto artboard = file->artboard("MainArtboard")->instance(); + REQUIRE(artboard != nullptr); + REQUIRE(artboard->stateMachineCount() == 1); + + // Test getting/setting number SMIInput via nested artboard path + auto numInput = artboard->getNumber("CircleOuterNumber", "CircleOuter"); + auto smiInput = artboard->input("CircleOuterNumber", "CircleOuter"); + auto smiNumInput = static_cast(smiInput); + auto nestedArtboard = artboard->nestedArtboardAtPath("CircleOuter"); + auto nestedInput = nestedArtboard->input("CircleOuterNumber")->as(); + REQUIRE(numInput->value() == 0); + REQUIRE(smiNumInput->value() == 0); + REQUIRE(nestedInput->nestedValue() == 0); + + numInput->value(10); + REQUIRE(numInput->value() == 10); + REQUIRE(smiNumInput->value() == 10); + REQUIRE(nestedInput->nestedValue() == 10); + + smiNumInput->value(5); + REQUIRE(numInput->value() == 5); + REQUIRE(smiNumInput->value() == 5); + REQUIRE(nestedInput->nestedValue() == 5); + + nestedInput->nestedValue(99); + REQUIRE(numInput->value() == 99); + REQUIRE(smiNumInput->value() == 99); + REQUIRE(nestedInput->nestedValue() == 99); +} + +TEST_CASE("validate nested trigger fire", "[nestedInput]") +{ + auto file = ReadRiveFile("../../test/assets/runtime_nested_inputs.riv"); + + auto artboard = file->artboard("MainArtboard")->instance(); + REQUIRE(artboard != nullptr); + REQUIRE(artboard->stateMachineCount() == 1); + + // Test getting/setting number SMIInput via nested artboard path + auto tInput = artboard->getTrigger("CircleOuterTrigger", "CircleOuter"); + auto smiInput = artboard->input("CircleOuterTrigger", "CircleOuter"); + auto smiTInput = static_cast(smiInput); + auto nestedArtboard = artboard->nestedArtboardAtPath("CircleOuter"); + auto nestedInput = nestedArtboard->input("CircleOuterTrigger")->as(); + auto nestedSMI = static_cast(nestedInput->input()); + REQUIRE(tInput->didFire() == false); + REQUIRE(smiTInput->didFire() == false); + REQUIRE(nestedSMI->didFire() == false); + + tInput->fire(); + REQUIRE(tInput->didFire() == true); + REQUIRE(smiTInput->didFire() == true); + REQUIRE(nestedSMI->didFire() == true); +} + +TEST_CASE("validate nested boolean get/set multiple nested artboards deep", "[nestedInput]") +{ + auto file = ReadRiveFile("../../test/assets/runtime_nested_inputs.riv"); + + auto artboard = file->artboard("MainArtboard")->instance(); + REQUIRE(artboard != nullptr); + REQUIRE(artboard->stateMachineCount() == 1); + + // Test getting/setting boolean SMIInput via nested artboard path + auto boolInput = artboard->getBool("CircleInnerState", "CircleOuter/CircleInner"); + auto smiInput = artboard->input("CircleInnerState", "CircleOuter/CircleInner"); + auto smiBoolInput = static_cast(smiInput); + auto nestedArtboard = artboard->nestedArtboardAtPath("CircleOuter/CircleInner"); + auto nestedInput = nestedArtboard->input("CircleInnerState")->as(); + REQUIRE(boolInput->value() == false); + REQUIRE(smiBoolInput->value() == false); + REQUIRE(nestedInput->nestedValue() == false); + + boolInput->value(true); + REQUIRE(boolInput->value() == true); + REQUIRE(smiBoolInput->value() == true); + REQUIRE(nestedInput->nestedValue() == true); + + smiBoolInput->value(false); + REQUIRE(boolInput->value() == false); + REQUIRE(smiBoolInput->value() == false); + REQUIRE(nestedInput->nestedValue() == false); + + nestedInput->nestedValue(true); + REQUIRE(boolInput->value() == true); + REQUIRE(smiBoolInput->value() == true); + REQUIRE(nestedInput->nestedValue() == true); +} \ No newline at end of file diff --git a/test/nested_text_run_test.cpp b/test/nested_text_run_test.cpp new file mode 100644 index 00000000..f79a6e6a --- /dev/null +++ b/test/nested_text_run_test.cpp @@ -0,0 +1,61 @@ +#include "rive/core/binary_reader.hpp" +#include "rive/file.hpp" +#include "rive/nested_artboard.hpp" +#include "rive/text/text_value_run.hpp" +#include "rive/animation/state_machine_instance.hpp" +#include "rive/animation/state_machine_input_instance.hpp" +#include "catch.hpp" +#include "rive_file_reader.hpp" +#include + +TEST_CASE("validate nested text get/set", "[nestedText]") +{ + auto file = ReadRiveFile("../../test/assets/runtime_nested_text_runs.riv"); + + auto artboard = file->artboard("ArtboardA")->instance(); + REQUIRE(artboard != nullptr); + REQUIRE(artboard->stateMachineCount() == 1); + + // Test getting/setting TextValueRun view nested artboard path one level deep + + auto textRunB1 = artboard->getTextRun("ArtboardBRun", "ArtboardB-1"); + REQUIRE(textRunB1->is()); + REQUIRE(textRunB1->text() == "Artboard B Run"); + + auto textRunB2 = artboard->getTextRun("ArtboardBRun", "ArtboardB-2"); + REQUIRE(textRunB2->is()); + REQUIRE(textRunB2->text() == "Artboard B Run"); + + // Test getting/setting TextValueRun view nested artboard path two level deep + + auto textRunB1C1 = artboard->getTextRun("ArtboardCRun", "ArtboardB-1/ArtboardC-1"); + REQUIRE(textRunB1C1->is()); + REQUIRE(textRunB1C1->text() == "Artboard C Run"); + + auto textRunB1C2 = artboard->getTextRun("ArtboardCRun", "ArtboardB-1/ArtboardC-2"); + REQUIRE(textRunB1C2->is()); + REQUIRE(textRunB1C2->text() == "Artboard C Run"); + + auto textRunB2C1 = artboard->getTextRun("ArtboardCRun", "ArtboardB-2/ArtboardC-1"); + REQUIRE(textRunB2C1->is()); + REQUIRE(textRunB2C1->text() == "Artboard C Run"); + + auto textRunB2C2 = artboard->getTextRun("ArtboardCRun", "ArtboardB-2/ArtboardC-2"); + REQUIRE(textRunB2C2->is()); + REQUIRE(textRunB2C2->text() == "Artboard C Run"); + + // Validate that text run values can be set + + textRunB1->text("Artboard B1 Run Updated"); + textRunB2->text("Artboard B2 Run Updated"); + textRunB1C1->text("Artboard B1C1 Run Updated"); + textRunB1C2->text("Artboard B1C2 Run Updated"); + textRunB2C1->text("Artboard B2C1 Run Updated"); + textRunB2C2->text("Artboard B2C2 Run Updated"); + REQUIRE(textRunB1->text() == "Artboard B1 Run Updated"); + REQUIRE(textRunB2->text() == "Artboard B2 Run Updated"); + REQUIRE(textRunB1C1->text() == "Artboard B1C1 Run Updated"); + REQUIRE(textRunB1C2->text() == "Artboard B1C2 Run Updated"); + REQUIRE(textRunB2C1->text() == "Artboard B2C1 Run Updated"); + REQUIRE(textRunB2C2->text() == "Artboard B2C2 Run Updated"); +} \ No newline at end of file diff --git a/test/path_test.cpp b/test/path_test.cpp index bd580786..a6640753 100644 --- a/test/path_test.cpp +++ b/test/path_test.cpp @@ -12,6 +12,7 @@ #include #include #include "rive_file_reader.hpp" +#include "rive/math/path_types.hpp" #include #include @@ -111,18 +112,16 @@ TEST_CASE("rectangle path builds expected commands", "[path]") artboard.advance(0.0f); - REQUIRE(rectangle->commandPath() != nullptr); + auto rawPath = rectangle->rawPath(); - auto path = static_cast(rectangle->commandPath()); - - REQUIRE(path->commands.size() == 7); - REQUIRE(path->commands[0].command == TestPathCommandType::Reset); - REQUIRE(path->commands[1].command == TestPathCommandType::MoveTo); - REQUIRE(path->commands[2].command == TestPathCommandType::LineTo); - REQUIRE(path->commands[3].command == TestPathCommandType::LineTo); - REQUIRE(path->commands[4].command == TestPathCommandType::LineTo); - REQUIRE(path->commands[5].command == TestPathCommandType::LineTo); - REQUIRE(path->commands[6].command == TestPathCommandType::Close); + auto verbs = rawPath.verbs(); + REQUIRE(verbs.size() == 6); + REQUIRE(verbs[0] == rive::PathVerb::move); + REQUIRE(verbs[1] == rive::PathVerb::line); + REQUIRE(verbs[2] == rive::PathVerb::line); + REQUIRE(verbs[3] == rive::PathVerb::line); + REQUIRE(verbs[4] == rive::PathVerb::line); + REQUIRE(verbs[5] == rive::PathVerb::close); } TEST_CASE("rounded rectangle path builds expected commands", "[path]") @@ -148,9 +147,7 @@ TEST_CASE("rounded rectangle path builds expected commands", "[path]") artboard.advance(0.0f); - REQUIRE(rectangle->commandPath() != nullptr); - - auto path = static_cast(rectangle->commandPath()); + auto rawPath = rectangle->rawPath(); // rewind // moveTo @@ -161,31 +158,30 @@ TEST_CASE("rounded rectangle path builds expected commands", "[path]") // lineTo, cubicTo for 4th corner // close - - REQUIRE(path->commands.size() == 11); + auto verbs = rawPath.verbs(); + REQUIRE(verbs.size() == 10); // Init - REQUIRE(path->commands[0].command == TestPathCommandType::Reset); - REQUIRE(path->commands[1].command == TestPathCommandType::MoveTo); + REQUIRE(verbs[0] == rive::PathVerb::move); // 1st - REQUIRE(path->commands[2].command == TestPathCommandType::CubicTo); + REQUIRE(verbs[1] == rive::PathVerb::cubic); // 2nd - REQUIRE(path->commands[3].command == TestPathCommandType::LineTo); - REQUIRE(path->commands[4].command == TestPathCommandType::CubicTo); + REQUIRE(verbs[2] == rive::PathVerb::line); + REQUIRE(verbs[3] == rive::PathVerb::cubic); // 3rd - REQUIRE(path->commands[5].command == TestPathCommandType::LineTo); - REQUIRE(path->commands[6].command == TestPathCommandType::CubicTo); + REQUIRE(verbs[4] == rive::PathVerb::line); + REQUIRE(verbs[5] == rive::PathVerb::cubic); // 4th - REQUIRE(path->commands[7].command == TestPathCommandType::LineTo); - REQUIRE(path->commands[8].command == TestPathCommandType::CubicTo); + REQUIRE(verbs[6] == rive::PathVerb::line); + REQUIRE(verbs[7] == rive::PathVerb::cubic); - REQUIRE(path->commands[9].command == TestPathCommandType::LineTo); + REQUIRE(verbs[8] == rive::PathVerb::line); - REQUIRE(path->commands[10].command == TestPathCommandType::Close); + REQUIRE(verbs[9] == rive::PathVerb::close); } TEST_CASE("ellipse path builds expected commands", "[path]") @@ -209,9 +205,7 @@ TEST_CASE("ellipse path builds expected commands", "[path]") artboard.advance(0.0f); - REQUIRE(ellipse->commandPath() != nullptr); - - auto path = static_cast(ellipse->commandPath()); + auto path = ellipse->rawPath(); // rewind // moveTo @@ -223,51 +217,52 @@ TEST_CASE("ellipse path builds expected commands", "[path]") // close - REQUIRE(path->commands.size() == 7); + auto verbs = path.verbs(); + auto points = path.points(); + REQUIRE(verbs.size() == 6); // Init - REQUIRE(path->commands[0].command == TestPathCommandType::Reset); - REQUIRE(path->commands[1].command == TestPathCommandType::MoveTo); - REQUIRE(path->commands[1].x == 0.0f); - REQUIRE(path->commands[1].y == -100.0f); + REQUIRE(verbs[0] == rive::PathVerb::move); + REQUIRE(points[0].x == 0.0f); + REQUIRE(points[0].y == -100.0f); // 1st - REQUIRE(path->commands[2].command == TestPathCommandType::CubicTo); - REQUIRE(path->commands[2].outX == 50.0f * rive::circleConstant); - REQUIRE(path->commands[2].outY == -100.0f); - REQUIRE(path->commands[2].inX == 50.0f); - REQUIRE(path->commands[2].inY == -100.0f * rive::circleConstant); - REQUIRE(path->commands[2].x == 50.0f); - REQUIRE(path->commands[2].y == 0.0f); + REQUIRE(verbs[1] == rive::PathVerb::cubic); + REQUIRE(points[1].x == 50.0f * rive::circleConstant); + REQUIRE(points[1].y == -100.0f); + REQUIRE(points[2].x == 50.0f); + REQUIRE(points[2].y == -100.0f * rive::circleConstant); + REQUIRE(points[3].x == 50.0f); + REQUIRE(points[3].y == 0.0f); // 2nd - REQUIRE(path->commands[3].command == TestPathCommandType::CubicTo); - REQUIRE(path->commands[3].outX == 50.0f); - REQUIRE(path->commands[3].outY == 100.0f * rive::circleConstant); - REQUIRE(path->commands[3].inX == 50.0f * rive::circleConstant); - REQUIRE(path->commands[3].inY == 100.0f); - REQUIRE(path->commands[3].x == 0.0f); - REQUIRE(path->commands[3].y == 100.0f); + REQUIRE(verbs[2] == rive::PathVerb::cubic); + REQUIRE(points[4].x == 50.0f); + REQUIRE(points[4].y == 100.0f * rive::circleConstant); + REQUIRE(points[5].x == 50.0f * rive::circleConstant); + REQUIRE(points[5].y == 100.0f); + REQUIRE(points[6].x == 0.0f); + REQUIRE(points[6].y == 100.0f); // 3rd - REQUIRE(path->commands[4].command == TestPathCommandType::CubicTo); - REQUIRE(path->commands[4].outX == -50.0f * rive::circleConstant); - REQUIRE(path->commands[4].outY == 100.0f); - REQUIRE(path->commands[4].inX == -50.0f); - REQUIRE(path->commands[4].inY == 100.0f * rive::circleConstant); - REQUIRE(path->commands[4].x == -50.0f); - REQUIRE(path->commands[4].y == 0.0f); + REQUIRE(verbs[3] == rive::PathVerb::cubic); + REQUIRE(points[7].x == -50.0f * rive::circleConstant); + REQUIRE(points[7].y == 100.0f); + REQUIRE(points[8].x == -50.0f); + REQUIRE(points[8].y == 100.0f * rive::circleConstant); + REQUIRE(points[9].x == -50.0f); + REQUIRE(points[9].y == 0.0f); // 4th - REQUIRE(path->commands[5].command == TestPathCommandType::CubicTo); - REQUIRE(path->commands[5].outX == -50.0f); - REQUIRE(path->commands[5].outY == -100.0f * rive::circleConstant); - REQUIRE(path->commands[5].inX == -50.0f * rive::circleConstant); - REQUIRE(path->commands[5].inY == -100.0f); - REQUIRE(path->commands[5].x == 0.0f); - REQUIRE(path->commands[5].y == -100.0f); - - REQUIRE(path->commands[6].command == TestPathCommandType::Close); + REQUIRE(verbs[4] == rive::PathVerb::cubic); + REQUIRE(points[10].x == -50.0f); + REQUIRE(points[10].y == -100.0f * rive::circleConstant); + REQUIRE(points[11].x == -50.0f * rive::circleConstant); + REQUIRE(points[11].y == -100.0f); + REQUIRE(points[12].x == 0.0f); + REQUIRE(points[12].y == -100.0f); + + REQUIRE(verbs[5] == rive::PathVerb::close); } TEST_CASE("nested solo with shape expanded and path collapsed", "[path]") @@ -295,7 +290,7 @@ TEST_CASE("nested solo with shape expanded and path collapsed", "[path]") auto path = solo->children()[1]->as(); REQUIRE(rectangleShape->isCollapsed() == false); REQUIRE(path->isCollapsed() == true); - REQUIRE(path->commandPath() != nullptr); + auto pathComposer = rootShape->pathComposer(); auto pathComposerPath = static_cast(pathComposer->localPath()); // Path is skipped and the nested shape forms its own drawable, so size is 0 @@ -327,7 +322,7 @@ TEST_CASE("nested solo clipping with shape collapsed and path expanded", "[path] auto path = solo->children()[1]->as(); REQUIRE(rectangleShape->isCollapsed() == true); REQUIRE(path->isCollapsed() == false); - REQUIRE(path->commandPath() != nullptr); + auto clippingShape = rectangleClip->clippingShapes()[0]; REQUIRE(clippingShape != nullptr); auto clippingPath = static_cast(clippingShape->renderPath()); diff --git a/test/rive_file_reader.hpp b/test/rive_file_reader.hpp index 6ba4c949..19001f03 100644 --- a/test/rive_file_reader.hpp +++ b/test/rive_file_reader.hpp @@ -8,16 +8,8 @@ static rive::NoOpFactory gNoOpFactory; -static inline std::unique_ptr ReadRiveFile(const char path[], - rive::Factory* factory = nullptr, - rive::FileAssetLoader* loader = nullptr, - bool loadInBandAssets = true) +static inline std::vector ReadFile(const char path[]) { - if (!factory) - { - factory = &gNoOpFactory; - } - FILE* fp = fopen(path, "rb"); REQUIRE(fp != nullptr); @@ -28,6 +20,21 @@ static inline std::unique_ptr ReadRiveFile(const char path[], REQUIRE(fread(bytes.data(), 1, length, fp) == length); fclose(fp); + return bytes; +} + +static inline std::unique_ptr ReadRiveFile(const char path[], + rive::Factory* factory = nullptr, + rive::FileAssetLoader* loader = nullptr, + bool loadInBandAssets = true) +{ + if (!factory) + { + factory = &gNoOpFactory; + } + + std::vector bytes = ReadFile(path); + rive::ImportResult result; auto file = rive::File::import(bytes, factory, &result, loader); REQUIRE(result == rive::ImportResult::success); diff --git a/test/simd_test.cpp b/test/simd_test.cpp index e86aa7d9..2192f120 100644 --- a/test/simd_test.cpp +++ b/test/simd_test.cpp @@ -31,6 +31,8 @@ namespace rive { constexpr float kInf = std::numeric_limits::infinity(); constexpr float kNaN = std::numeric_limits::quiet_NaN(); +constexpr double kInf_double = std::numeric_limits::infinity(); +constexpr double kNaN_double = std::numeric_limits::quiet_NaN(); // Check simd::any. TEST_CASE("any", "[simd]") @@ -295,21 +297,47 @@ TEST_CASE("min-max", "[simd]") CHECK_ALL((f4.xyz == vec<3>{1, 1, 2})); CHECK(std::isnan(f4.w)); + // fminf/fmaxf behaves the same as simd::min/max. // simd::min/max differs from std::min/max when the first argument is NaN. - CHECK(simd::min(kNaN, 1).x == 1); - CHECK(std::isnan(std::min(kNaN, 1))); - CHECK(simd::max(kNaN, 1).x == 1); - CHECK(std::isnan(std::max(kNaN, 1))); - CHECK(simd::min(kNaN, 1).x == 1); - CHECK(std::isnan(std::min(kNaN, 1))); - CHECK(simd::max(kNaN, 1).x == 1); - CHECK(std::isnan(std::max(kNaN, 1))); - - // simd::min/max is equivalent std::min/max when the second argument is NaN. - CHECK(simd::min(1, kNaN).x == std::min(1, kNaN)); - CHECK(simd::max(1, kNaN).x == std::max(1, kNaN)); - CHECK(simd::min(1, kNaN).x == std::min(1, kNaN)); - CHECK(simd::max(1, kNaN).x == std::max(1, kNaN)); + for (float f : {-2.f, -kInf, kInf}) + { + CHECK(simd::min(kNaN, f).x == f); + CHECK(fminf(kNaN, f) == f); + CHECK(std::isnan(std::min(kNaN, f))); + CHECK(simd::max(kNaN, f).x == f); + CHECK(fmaxf(kNaN, f) == f); + CHECK(std::isnan(std::max(kNaN, f))); + } + for (double d : {-1.0, -kInf_double, kInf_double}) + { + CHECK(simd::min(kNaN_double, d).x == d); + CHECK(fmin(kNaN_double, d) == d); + CHECK(std::isnan(std::min(kNaN_double, d))); + CHECK(simd::max(kNaN_double, d).x == d); + CHECK(fmax(kNaN_double, d) == d); + CHECK(std::isnan(std::max(kNaN_double, d))); + } + + // fminf/fmaxf/std::min/std::max/simd::min/stmd::max all behave the same when the second + // argument is NaN. + for (float f : {1.f, -kInf, kInf}) + { + CHECK(simd::min(f, kNaN).x == f); + CHECK(fminf(f, kNaN) == f); + CHECK(std::min(f, kNaN) == f); + CHECK(simd::max(f, kNaN).x == f); + CHECK(fmaxf(f, kNaN) == f); + CHECK(std::max(f, kNaN) == f); + } + for (double d : {2.0, -kInf_double, kInf_double}) + { + CHECK(simd::min(d, kNaN_double).x == d); + CHECK(fmin(d, kNaN_double) == d); + CHECK(std::min(d, kNaN_double) == d); + CHECK(simd::max(d, kNaN_double).x == d); + CHECK(fmax(d, kNaN_double) == d); + CHECK(std::max(d, kNaN_double) == d); + } // check non-32-bit types. CHECK_ALL((simd::max(simd::gvec{3, 4}, simd::gvec{4, 3}) == @@ -341,11 +369,17 @@ TEST_CASE("clamp", "[simd]") // Returns lo if x == NaN, but std::clamp() returns NaN. CHECK(simd::clamp(kNaN, 1, 2).x == 1); + // Matches math::clamp(). + CHECK(simd::clamp(kNaN, 1, 2).x == math::clamp(kNaN, 1, 2)); // Returns hi if hi <= lo. CHECK(simd::clamp(3, 2, 1).x == 1); CHECK(simd::clamp(kNaN, 2, 1).x == 1); CHECK(simd::clamp(kNaN, kNaN, 1).x == 1); + // Matches math::clamp(). + CHECK(simd::clamp(3, 2, 1).x == math::clamp(3, 2, 1)); + CHECK(simd::clamp(kNaN, 2, 1).x == math::clamp(kNaN, 2, 1)); + CHECK(simd::clamp(kNaN, kNaN, 1).x == math::clamp(kNaN, kNaN, 1)); // Ignores hi and/or lo if they are NaN. CHECK(simd::clamp(3, 4, kNaN).x == 4); @@ -353,6 +387,12 @@ TEST_CASE("clamp", "[simd]") CHECK(simd::clamp(3, kNaN, 2).x == 2); CHECK(simd::clamp(3, kNaN, 4).x == 3); CHECK(simd::clamp(3, kNaN, kNaN).x == 3); + // Matches math::clamp(). + CHECK(simd::clamp(3, 4, kNaN).x == math::clamp(3, 4, kNaN)); + CHECK(simd::clamp(3, 2, kNaN).x == math::clamp(3, 2, kNaN)); + CHECK(simd::clamp(3, kNaN, 2).x == math::clamp(3, kNaN, 2)); + CHECK(simd::clamp(3, kNaN, 4).x == math::clamp(3, kNaN, 4)); + CHECK(simd::clamp(3, kNaN, kNaN).x == math::clamp(3, kNaN, kNaN)); } // Check simd::abs. diff --git a/test/solo_test.cpp b/test/solo_test.cpp index e53b2509..30d57b01 100644 --- a/test/solo_test.cpp +++ b/test/solo_test.cpp @@ -237,9 +237,9 @@ TEST_CASE("hit test on nested artboards in solos", "[solo]") REQUIRE(artboard->is()); REQUIRE(artboard->find("Nested-Artboard-Active") != nullptr); auto nestedArtboardActive = artboard->find("Nested-Artboard-Active"); - REQUIRE(nestedArtboardActive->artboard() != nullptr); + REQUIRE(nestedArtboardActive->artboardInstance() != nullptr); - auto nestedArtboardActiveArtboardInstance = nestedArtboardActive->artboard(); + auto nestedArtboardActiveArtboardInstance = nestedArtboardActive->artboardInstance(); auto activeRect = nestedArtboardActiveArtboardInstance->find("Clickable-Rectangle"); REQUIRE(activeRect != nullptr); @@ -250,8 +250,8 @@ TEST_CASE("hit test on nested artboards in solos", "[solo]") REQUIRE(artboard->find("Nested-Artboard-Inactive") != nullptr); auto nestedArtboardInactive = artboard->find("Nested-Artboard-Inactive"); - REQUIRE(nestedArtboardInactive->artboard() != nullptr); - auto nestedArtboardInactiveArtboardInstance = nestedArtboardInactive->artboard(); + REQUIRE(nestedArtboardInactive->artboardInstance() != nullptr); + auto nestedArtboardInactiveArtboardInstance = nestedArtboardInactive->artboardInstance(); auto inactiveRect = nestedArtboardInactiveArtboardInstance->find("Clickable-Rectangle"); REQUIRE(inactiveRect != nullptr); diff --git a/test/state_machine_event_test.cpp b/test/state_machine_event_test.cpp index 3a6e2c22..e4551d87 100644 --- a/test/state_machine_event_test.cpp +++ b/test/state_machine_event_test.cpp @@ -16,6 +16,7 @@ #include "rive/animation/blend_state_transition.hpp" #include "rive/animation/listener_input_change.hpp" #include "rive/animation/listener_fire_event.hpp" +#include "rive/animation/nested_state_machine.hpp" #include "rive/animation/entry_state.hpp" #include "rive/node.hpp" #include "catch.hpp" @@ -259,3 +260,63 @@ TEST_CASE("timeline events load correctly and report", "[events]") // Event should've occurred right at 0.5 seconds. REQUIRE(stateMachineInstance->reportedEventAt(0).secondsDelay() == Approx(0.1f)); } + +TEST_CASE("events from a nested artboard propagate to a listener on a parent", "[events]") +{ + auto file = ReadRiveFile("../../test/assets/nested_event_test.riv"); + + auto artboard = file->artboard()->instance(); + REQUIRE(artboard != nullptr); + REQUIRE(artboard->stateMachineCount() == 1); + + auto stateMachineInstance = artboard->stateMachineAt(0); + REQUIRE(stateMachineInstance != nullptr); + REQUIRE(stateMachineInstance->stateMachine()->inputCount() == 1); + + // Input is on the main artboard + auto input = stateMachineInstance->getBool("Boolean 1"); + REQUIRE(input->value() == false); + + artboard->advance(0.0f); + stateMachineInstance->advance(0.0f); + + auto nested = artboard->find(); + REQUIRE(nested.size() == 1); + auto nestedArtboard = nested[0]->artboardInstance(); + auto nestedStateMachineInstance = + nested[0]->nestedAnimations()[0]->as()->stateMachineInstance(); + REQUIRE(nestedStateMachineInstance != nullptr); + auto events = nestedArtboard->find(); + REQUIRE(events.size() == 1); + + // Validate listener on the nested artboard + REQUIRE(nestedStateMachineInstance->stateMachine()->listenerCount() == 1); + auto listener1 = nestedStateMachineInstance->stateMachine()->listener(0); + auto target1 = nestedArtboard->resolve(listener1->targetId()); + REQUIRE(target1->is()); + REQUIRE(listener1->actionCount() == 1); + auto fireEvent1 = listener1->action(0); + REQUIRE(fireEvent1 != nullptr); + REQUIRE(fireEvent1->is()); + REQUIRE(fireEvent1->as()->eventId() != 0); + auto event = nestedArtboard->resolve(fireEvent1->as()->eventId()); + REQUIRE(event->is()); + REQUIRE(event->as()->name() == "NestedEvent"); + + // Validate the event is reported to the nested artboard + REQUIRE(nestedStateMachineInstance->reportedEventCount() == 0); + stateMachineInstance->pointerDown(rive::Vec2D(250.0f, 100.0f)); + REQUIRE(nestedStateMachineInstance->reportedEventCount() == 1); + auto nestedReportedEvent1 = nestedStateMachineInstance->reportedEventAt(0); + REQUIRE(nestedReportedEvent1.event()->name() == "NestedEvent"); + + artboard->advance(0.0f); + + // Validate the input on the main artboard updates as a result of the event + // from the nested artboard + REQUIRE(input->value() == true); + + // After advancing again the reportedEventCount should return to 0. + stateMachineInstance->advance(0.0f); + REQUIRE(stateMachineInstance->reportedEventCount() == 0); +} diff --git a/test/state_machine_test.cpp b/test/state_machine_test.cpp index e3dd9cf1..6f25ae84 100644 --- a/test/state_machine_test.cpp +++ b/test/state_machine_test.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -242,3 +243,174 @@ TEST_CASE("Transitions with duration completes the state correctly before changi delete stateMachineInstance; } + +TEST_CASE("Blend state animations with reset applied to them.", "[file]") +{ + auto file = ReadRiveFile("../../test/assets/animation_reset_cases.riv"); + + auto artboard = file->artboard(); + auto stateMachine = artboard->stateMachine("blend-states-state-machine"); + + // We empty all factory reset resources to start the test clean + rive::AnimationResetFactory::releaseResources(); + REQUIRE(rive::AnimationResetFactory::resourcesCount() == 0); + + REQUIRE(artboard != nullptr); + REQUIRE(artboard->animationCount() == 9); + REQUIRE(artboard->stateMachineCount() == 1); + + auto abi = artboard->instance(); + rive::StateMachineInstance* stateMachineInstance = + new rive::StateMachineInstance(stateMachine, abi.get()); + stateMachineInstance->advanceAndApply(0.1f); + abi->advance(0.1f); + + auto blendValueNumber = stateMachineInstance->getNumber("blend-value"); + REQUIRE(blendValueNumber != nullptr); + REQUIRE(blendValueNumber->value() == 0); + blendValueNumber->value(50); + REQUIRE(blendValueNumber->value() == 50); + stateMachineInstance->advanceAndApply(0.1f); + abi->advance(0.1f); + + auto rect1 = abi->children()[9]->as(); + REQUIRE(rect1->name() == "rect1"); + + auto rect2 = abi->children()[7]->as(); + REQUIRE(rect2->name() == "rect2"); + + auto triangle = abi->children()[5]->as(); + REQUIRE(triangle->name() == "triangle"); + + // This blend rotates 2 * Pi. At 50% it should have rotated 1 * Pi + REQUIRE(rect1->rotation() == Approx(3.141592f)); + + auto state2Bool = stateMachineInstance->getBool("state-2"); + REQUIRE(state2Bool != nullptr); + REQUIRE(state2Bool->value() == false); + state2Bool->value(true); + blendValueNumber->value(50); + stateMachineInstance->advanceAndApply(0.1f); + abi->advance(0.1f); + stateMachineInstance->advanceAndApply(0.1f); + REQUIRE(rive::AnimationResetFactory::resourcesCount() == 0); + REQUIRE(state2Bool->value() == true); + // X and Y interpolation in this state ranges are [75; 425] and [50; 450] + // so at 50% they should be at 250, 250 + REQUIRE(rect1->x() == 250.0f); + REQUIRE(rect1->y() == 250.0f); + // rect2 rotation range is [0; -360] + // so at 50% it should be at -180 + REQUIRE(rect2->rotation() == Approx(-3.141592f)); + + auto state3Bool = stateMachineInstance->getBool("state-3"); + REQUIRE(state3Bool != nullptr); + REQUIRE(state3Bool->value() == false); + state3Bool->value(true); + REQUIRE(rive::AnimationResetFactory::resourcesCount() == 0); + stateMachineInstance->advanceAndApply(0.1f); + abi->advance(0.1f); + stateMachineInstance->advanceAndApply(0.1f); + blendValueNumber->value(100); + abi->advance(0.1f); + stateMachineInstance->advanceAndApply(0.1f); + REQUIRE(state3Bool->value() == true); + REQUIRE(triangle->y() == Approx(43.13281f)); + + REQUIRE(rive::AnimationResetFactory::resourcesCount() == 1); + + auto state4Bool = stateMachineInstance->getBool("state-4"); + REQUIRE(state4Bool != nullptr); + REQUIRE(state4Bool->value() == false); + state4Bool->value(true); + stateMachineInstance->advanceAndApply(0.1f); + abi->advance(0.1f); + REQUIRE(state4Bool->value() == true); + REQUIRE(rive::AnimationResetFactory::resourcesCount() == 2); + + state4Bool->value(false); + stateMachineInstance->advanceAndApply(0.1f); + abi->advance(0.1f); + stateMachineInstance->advanceAndApply(0.1f); + REQUIRE(state4Bool->value() == false); + // After switching states mutiple times resources stay at 2 because they are released + // and retrieved from the pool + REQUIRE(rive::AnimationResetFactory::resourcesCount() == 2); + delete stateMachineInstance; +} + +TEST_CASE("Transitions with reset applied to them.", "[file]") +{ + auto file = ReadRiveFile("../../test/assets/animation_reset_cases.riv"); + + auto artboard = file->artboard("transitions"); + auto stateMachine = artboard->stateMachine("transitions-state-machine"); + + // We empty all factory reset resources to start the test clean + rive::AnimationResetFactory::releaseResources(); + REQUIRE(rive::AnimationResetFactory::resourcesCount() == 0); + + REQUIRE(artboard != nullptr); + REQUIRE(artboard->animationCount() == 5); + REQUIRE(artboard->stateMachineCount() == 1); + + auto abi = artboard->instance(); + rive::StateMachineInstance* stateMachineInstance = + new rive::StateMachineInstance(stateMachine, abi.get()); + stateMachineInstance->advanceAndApply(0.1f); + abi->advance(0.1f); + + auto rect = abi->children()[7]->as(); + REQUIRE(rect->name() == "rectangle"); + REQUIRE(rect->x() == 50); + + auto ellipse = abi->children()[5]->as(); + REQUIRE(ellipse->name() == "ellipse"); + REQUIRE(ellipse->x() == Approx(440.31241)); + + auto stateNumber = stateMachineInstance->getNumber("Number 1"); + REQUIRE(stateNumber != nullptr); + REQUIRE(stateNumber->value() == 0); + stateNumber->value(1); + REQUIRE(stateNumber->value() == 1); + stateMachineInstance->advanceAndApply(0.1f); + abi->advance(0.1f); + stateMachineInstance->advanceAndApply(1.25f); + + // rect transitions in 2.5 secs from x->50 to x->433 + // so if the translation is linear, after 1.25s it should have + // traversed half the path + REQUIRE(rect->x() == 241.5f); + + stateNumber->value(2); + REQUIRE(stateNumber->value() == 2); + stateMachineInstance->advanceAndApply(0.1f); + abi->advance(0.1f); + stateMachineInstance->advanceAndApply(1.25f); + // range is [440.31241; 42.69] + // half if the path is 42.69 + (440.21241 - 42.60) = 241.4962 + REQUIRE(ellipse->x() == Approx(241.49992f)); + + // Transitions release their instance immediately so it's available for the next instance to use + REQUIRE(rive::AnimationResetFactory::resourcesCount() == 0); + + stateNumber->value(3); + REQUIRE(stateNumber->value() == 3); + stateMachineInstance->advanceAndApply(0.1f); + abi->advance(0.1f); + stateMachineInstance->advanceAndApply(1.25f); + + REQUIRE(rive::AnimationResetFactory::resourcesCount() == 0); + + stateNumber->value(4); + REQUIRE(stateNumber->value() == 4); + stateMachineInstance->advanceAndApply(0.1f); + abi->advance(0.1f); + stateMachineInstance->advanceAndApply(1.25f); + + // The last two states don't have a transition with duration set so the instance is released + // and available + REQUIRE(rive::AnimationResetFactory::resourcesCount() == 1); + + delete stateMachineInstance; +} diff --git a/viewer/build/macosx/build_viewer.sh b/viewer/build/macosx/build_viewer.sh index f41f0fe1..80539470 100755 --- a/viewer/build/macosx/build_viewer.sh +++ b/viewer/build/macosx/build_viewer.sh @@ -57,7 +57,7 @@ export PREMAKE=$DEPENDENCIES/bin/premake5 pushd .. OUT=out/$RENDERER/$GRAPHICS/$CONFIG -$PREMAKE --scripts=../../build --file=./premake5_viewer.lua --config=$CONFIG --out=$OUT gmake2 --graphics=$GRAPHICS --renderer=$RENDERER --with_rive_tools --with_rive_text --with_rive_audio=system +$PREMAKE --scripts=../../build --file=./premake5_viewer.lua --config=$CONFIG --out=$OUT gmake2 --graphics=$GRAPHICS --renderer=$RENDERER --with_rive_tools --with_rive_text --with_rive_audio=system --with_rive_layout for var in "$@"; do if [[ $var = "clean" ]]; then diff --git a/viewer/build/premake5_viewer.lua b/viewer/build/premake5_viewer.lua index 1f7b2d93..725da540 100644 --- a/viewer/build/premake5_viewer.lua +++ b/viewer/build/premake5_viewer.lua @@ -24,7 +24,7 @@ do end kind('ConsoleApp') - defines({ 'WITH_RIVE_TEXT', 'WITH_RIVE_AUDIO' }) + defines({ 'WITH_RIVE_TEXT', 'WITH_RIVE_AUDIO', 'WITH_RIVE_LAYOUT', 'YOGA_EXPORT=' }) includedirs({ '../include', @@ -34,9 +34,10 @@ do dependencies .. '/sokol', dependencies .. '/imgui', miniaudio, + yoga, }) - links({ 'rive', 'rive_harfbuzz', 'rive_sheenbidi' }) + links({ 'rive', 'rive_harfbuzz', 'rive_sheenbidi', 'rive_yoga' }) libdirs({ rive .. '/build/%{cfg.system}/bin/%{cfg.buildcfg}' }) @@ -78,7 +79,7 @@ do do includedirs({ rive_tess .. '/include', rive .. '/decoders/include' }) defines({ 'RIVE_RENDERER_TESS' }) - links({ 'rive_tess_renderer', 'rive_decoders', 'libpng', 'zlib' }) + links({ 'rive_tess_renderer', 'rive_decoders', 'libpng', 'zlib', 'libjpeg', 'libwebp' }) libdirs({ rive_tess .. '/build/%{cfg.system}/bin/%{cfg.buildcfg}' }) end diff --git a/viewer/src/viewer_content/scene_content.cpp b/viewer/src/viewer_content/scene_content.cpp index 392a6665..ee43e2e1 100644 --- a/viewer/src/viewer_content/scene_content.cpp +++ b/viewer/src/viewer_content/scene_content.cpp @@ -13,7 +13,10 @@ #include "rive/layout.hpp" #include "rive/math/aabb.hpp" #include "rive/assets/image_asset.hpp" +#include "rive/viewmodel/viewmodel_instance.hpp" #include "viewer/viewer_content.hpp" +#include "rive/relative_local_asset_loader.hpp" + #ifdef RIVE_RENDERER_TESS #include "viewer/sample_tools/sample_atlas_packer.hpp" #endif @@ -66,6 +69,7 @@ class SceneContent : public ViewerContent std::unique_ptr m_ArtboardInstance; std::unique_ptr m_CurrentScene; + rive::ViewModelInstance* m_ViewModelInstance; int m_ArtboardIndex = 0; int m_AnimationIndex = 0; int m_StateMachineIndex = -1; @@ -82,6 +86,9 @@ class SceneContent : public ViewerContent m_ArtboardIndex = (index == REQUEST_DEFAULT_SCENE) ? 0 : index; m_ArtboardInstance = m_File->artboardAt(m_ArtboardIndex); + // m_ViewModelInstance = m_File->viewModelInstanceNamed("vm-3"); + m_ViewModelInstance = m_File->createViewModelInstance(m_ArtboardInstance.get()); + m_ArtboardInstance->dataContextFromInstance(m_ViewModelInstance); m_ArtboardInstance->advance(0.0f); loadNames(m_ArtboardInstance.get()); @@ -382,7 +389,9 @@ class SceneContent : public ViewerContent std::unique_ptr ViewerContent::Scene(const char filename[]) { auto bytes = LoadFile(filename); - if (auto file = rive::File::import(bytes, RiveFactory())) + rive::RelativeLocalAssetLoader loader(filename); + rive::ImportResult result; + if (auto file = rive::File::import(bytes, RiveFactory(), &result, &loader)) { return rivestd::make_unique(filename, std::move(file)); } diff --git a/viewer/src/viewer_content/textpath_content.cpp b/viewer/src/viewer_content/textpath_content.cpp index 69fe3fd0..9b05375c 100644 --- a/viewer/src/viewer_content/textpath_content.cpp +++ b/viewer/src/viewer_content/textpath_content.cpp @@ -241,7 +241,7 @@ class TextPathContent : public ViewerContent RawPath warp = make_quad_path(m_pathpts); this->draw_warp(renderer, warp); - auto meas = ContourMeasureIter(warp).next(); + auto meas = ContourMeasureIter(&warp).next(); const float warpLength = meas->length(); const float textLength = gruns.back().xpos.back(); diff --git a/viewer/src/viewer_content/trimpath_content.cpp b/viewer/src/viewer_content/trimpath_content.cpp index af28c42e..d4ffb3d1 100644 --- a/viewer/src/viewer_content/trimpath_content.cpp +++ b/viewer/src/viewer_content/trimpath_content.cpp @@ -114,7 +114,7 @@ class TrimPathContent : public ViewerContent { renderer->save(); - auto cm = ContourMeasureIter(*p, false).next(); + auto cm = ContourMeasureIter(p, false).next(); auto p1 = trim(cm.get(), m_trimFrom, m_trimTo); stroke_path(renderer, p1, 20, 0xFFFF0000);