diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..d202a332 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every week + interval: "weekly" diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml new file mode 100644 index 00000000..136a3268 --- /dev/null +++ b/.github/workflows/build_and_test.yml @@ -0,0 +1,276 @@ +name: Pamplejuce + +on: + workflow_dispatch: # lets you run a build from the UI + push: + pull_request: + +# When pushing new commits, cancel any running builds on that branch +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + +env: + BUILD_TYPE: Release + BUILD_DIR: Builds + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DISPLAY: :0 # linux pluginval needs this + CMAKE_BUILD_PARALLEL_LEVEL: 3 # Use up to 3 cpus to build juceaide, etc + HOMEBREW_NO_INSTALL_CLEANUP: 1 + IPP_DIR: C:\Program Files (x86)\Intel\oneAPI\ipp\latest\lib\cmake\ipp + +# jobs are run in paralell on different machines +# all steps run in series +jobs: + build_and_test: + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false # show all errors for each platform (vs. cancel jobs on error) + matrix: + include: + - name: Linux + os: ubuntu-22.04 + pluginval-binary: ./pluginval + - name: macOS + os: macos-14 + pluginval-binary: pluginval.app/Contents/MacOS/pluginval + - name: Windows + os: windows-latest + pluginval-binary: ./pluginval.exe + + steps: + # This is just easier than debugging different compilers on different platforms + - name: Set up Clang + if: ${{ matrix.name != 'macOS' }} + uses: egor-tensin/setup-clang@v1 + + # This also starts up our "fake" display Xvfb, needed for pluginval + - name: Install JUCE's Linux Deps + if: runner.os == 'Linux' + # Thanks to McMartin & co https://forum.juce.com/t/list-of-juce-dependencies-under-linux/15121/44 + run: | + sudo apt-get update && sudo apt install libasound2-dev libx11-dev libxinerama-dev libxext-dev libfreetype6-dev libwebkit2gtk-4.0-dev libglu1-mesa-dev xvfb ninja-build + # downgrade gcc to workaround 22.04 and C++20 issue + # see: https://github.com/actions/runner-images/issues/8659 + sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list + sudo apt-get update + sudo apt-get install -y --allow-downgrades libc6=2.35-0ubuntu3.7 libc6-dev=2.35-0ubuntu3.7 libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04 + sudo /usr/bin/Xvfb $DISPLAY & + + - name: Cache IPP (Windows) + if: runner.os == 'Windows' + id: cache-ipp + uses: actions/cache@v4 + with: + key: ipp-v4 + path: C:\Program Files (x86)\Intel + + - name: Install IPP (Windows) + if: (runner.os == 'Windows') && (steps.cache-ipp.outputs.cache-hit != 'true') + shell: bash + run: | + curl --output oneapi.exe https://registrationcenter-download.intel.com/akdlm/IRC_NAS/8d158661-ca8f-4e66-b5ea-3e0b3d00836a/w_ipp_oneapi_p_2021.10.1.15_offline.exe + ./oneapi.exe -s -x -f oneapi + ./oneapi/bootstrapper.exe -s -c --action install --components=intel.oneapi.win.ipp.devel --eula=accept -p=NEED_VS2022_INTEGRATION=1 --log-dir=. + + - name: Save IPP cache (even on CI fail) + if: runner.os == 'Windows' && (steps.cache-ipp.outputs.cache-hit != 'true') + uses: actions/cache/save@v4 + with: + path: C:\Program Files (x86)\Intel + key: ipp-v4 + + - name: Install Ninja (Windows) + if: runner.os == 'Windows' + shell: bash + run: choco install ninja + + - name: Install macOS Deps + if: ${{ matrix.name == 'macOS' }} + run: brew install ninja osxutils + + # This block can be removed once 15.1 is default (JUCE requires it when building on macOS 14) + - name: Use latest Xcode on system (macOS) + if: ${{ matrix.name == 'macOS' }} + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: true # Get JUCE populated + + - name: Cache the build + uses: mozilla-actions/sccache-action@v0.0.4 + + - name: Import Certificates (macOS) + uses: apple-actions/import-codesign-certs@v2 # only exists as a tag right now + if: ${{ matrix.name == 'macOS' }} + with: + p12-file-base64: ${{ secrets.DEV_ID_APP_CERT }} + p12-password: ${{ secrets.DEV_ID_APP_PASSWORD }} + + - name: Configure + shell: bash + run: cmake -B ${{ env.BUILD_DIR }} -G Ninja -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE}} -DCMAKE_C_COMPILER_LAUNCHER=${{ matrix.ccache }} -DCMAKE_CXX_COMPILER_LAUNCHER=${{ matrix.ccache }} -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" . + + - name: Build + shell: bash + run: cmake --build ${{ env.BUILD_DIR }} --config ${{ env.BUILD_TYPE }} --parallel 4 + + - name: Test + working-directory: ${{ env.BUILD_DIR }} + run: ctest --output-on-failure -j4 -VV + + - name: Read in .env from CMake # see GitHubENV.cmake + shell: bash + run: | + cat .env # show us the config + cat .env >> $GITHUB_ENV # pull in our PRODUCT_NAME, etc + + - name: Set additional env vars for next steps + shell: bash + run: | + ARTIFACTS_PATH=${{ env.BUILD_DIR }}/${{ env.PROJECT_NAME }}_artefacts/${{ env.BUILD_TYPE }} + echo "ARTIFACTS_PATH=$ARTIFACTS_PATH" >> $GITHUB_ENV + echo "VST3_PATH=$ARTIFACTS_PATH/VST3/${{ env.PRODUCT_NAME }}.vst3" >> $GITHUB_ENV + echo "AU_PATH=$ARTIFACTS_PATH/AU/${{ env.PRODUCT_NAME }}.component" >> $GITHUB_ENV + echo "AUV3_PATH=$ARTIFACTS_PATH/AUv3/${{ env.PRODUCT_NAME }}.appex" >> $GITHUB_ENV + echo "STANDALONE_PATH=$ARTIFACTS_PATH/Standalone/${{ env.PRODUCT_NAME }}.app" >> $GITHUB_ENV + echo "ARTIFACT_NAME=${{ env.PRODUCT_NAME }}-${{ env.VERSION }}-${{ matrix.name }}" >> $GITHUB_ENV + + - name: Pluginval + shell: bash + run: | + curl -LO "https://github.com/Tracktion/pluginval/releases/download/v1.0.3/pluginval_${{ matrix.name }}.zip" + 7z x pluginval_${{ matrix.name }}.zip + ${{ matrix.pluginval-binary }} --strictness-level 10 --verbose --validate "${{ env.VST3_PATH }}" + + - name: Codesign (macOS) + if: ${{ matrix.name == 'macOS' }} + run: | + # Each plugin must be code signed + codesign --force -s "${{ secrets.DEVELOPER_ID_APPLICATION}}" -v "${{ env.VST3_PATH }}" --deep --strict --options=runtime --timestamp + codesign --force -s "${{ secrets.DEVELOPER_ID_APPLICATION}}" -v "${{ env.AU_PATH }}" --deep --strict --options=runtime --timestamp + codesign --force -s "${{ secrets.DEVELOPER_ID_APPLICATION}}" -v "${{ env.STANDALONE_PATH }}" --deep --strict --options=runtime --timestamp + + - name: Add Custom Icons (macOS) + if: ${{ matrix.name == 'macOS' }} + run: | + # add the icns as its own icon resource (meta!) + sips -i packaging/pamplejuce.icns + + # Grab the resource, put in tempfile + DeRez -only icns packaging/pamplejuce.icns > /tmp/icons + + # Stuff the resource into the strange Icon? file's resource fork + Rez -a /tmp/icons -o "${{ env.VST3_PATH }}/Icon"$'\r' + Rez -a /tmp/icons -o "${{ env.AU_PATH }}/Icon"$'\r' + + # Set custom icon attribute + SetFile -a C "${{ env.VST3_PATH }}" + SetFile -a C "${{ env.AU_PATH }}" + + - name: Create DMG, Notarize and Staple (macOS) + if: ${{ matrix.name == 'macOS' }} + run: | + # workaround for https://github.com/LinusU/node-appdmg/issues/234 + python3 -m pip install setuptools --break-system-packages + npm install -g appdmg + mkdir -p packaging/dmg + + # Create directories for the dmg symlinks + sudo mkdir -m 755 -p /Library/Audio/Plug-Ins/Components && sudo mkdir -m 755 -p /Library/Audio/Plug-Ins/VST3 + ln -s /Library/Audio/Plug-Ins/Components "packaging/dmg/Your Mac's Component folder" + ln -s /Library/Audio/Plug-Ins/VST3 "packaging/dmg/Your Mac's VST3 folder" + mv "${{ env.VST3_PATH }}" packaging/dmg + mv "${{ env.AU_PATH }}" packaging/dmg + mv "${{ env.STANDALONE_PATH }}" packaging/dmg + + # Run appdmg to create the .dmg + cd packaging && appdmg dmg.json "${{ env.ARTIFACT_NAME}}.dmg" + codesign -s "${{ secrets.DEVELOPER_ID_APPLICATION}}" --timestamp -i ${{ env.BUNDLE_ID }} --force "${{ env.ARTIFACT_NAME }}.dmg" + xcrun notarytool submit "${{ env.ARTIFACT_NAME }}.dmg" --apple-id ${{ secrets.NOTARIZATION_USERNAME }} --password ${{ secrets.NOTARIZATION_PASSWORD }} --team-id ${{ secrets.TEAM_ID }} --wait + xcrun stapler staple "${{ env.ARTIFACT_NAME }}.dmg" + + - name: Zip + if: ${{ matrix.name == 'Linux' }} + working-directory: ${{ env.ARTIFACTS_PATH }} + run: 7z a -tzip "${{ env.ARTIFACT_NAME }}.zip" "-xr!lib${{ env.PRODUCT_NAME }}_SharedCode.a" . + + - name: Generate Installer + if: ${{ matrix.name == 'Windows' }} + shell: bash + run: | + iscc "packaging\installer.iss" + mv "packaging/Output/${{ env.ARTIFACT_NAME }}.exe" "${{ env.ARTIFACTS_PATH }}/" + + - name: Codesign with Azure Trusted Signing + if: ${{ matrix.name == 'Windows' }} + uses: azure/trusted-signing-action@v0.3.16 + with: + # The Azure Active Directory tenant (directory) ID. + azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} + + # The client (application) ID of an App Registration in the tenant. + azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} + + # A client secret that was generated for the App Registration. + azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} + + # The Code Signing Account endpoint. The URI value must have a URI that aligns to the region your Code Signing Account and Certificate Profile you are specifying were created in during the setup of these resources. + endpoint: ${{ secrets.AZURE_ENDPOINT }} + + # The Code Signing Account name. + code-signing-account-name: ${{ secrets.AZURE_CODE_SIGNING_NAME }} + + # The Certificate Profile name. + certificate-profile-name: ${{ secrets.AZURE_CERT_PROFILE_NAME }} + + # This signs all exes inside the folder + files-folder: ${{ env.ARTIFACTS_PATH }} + files-folder-filter: exe + file-digest: SHA256 + + - name: Upload Exe (Windows) + if: ${{ matrix.name == 'Windows' }} + uses: actions/upload-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME }}.exe + path: "${{ env.ARTIFACTS_PATH }}/${{ env.ARTIFACT_NAME }}.exe" + + - name: Upload Zip (Linux) + if: ${{ matrix.name == 'Linux' }} + uses: actions/upload-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME }}.zip + path: "${{ env.ARTIFACTS_PATH }}/${{ env.ARTIFACT_NAME }}.zip" + + - name: Upload DMG (macOS) + if: ${{ matrix.name == 'macOS' }} + uses: actions/upload-artifact@v4 + with: + name: ${{ env.ARTIFACT_NAME }}.dmg + path: packaging/${{ env.ARTIFACT_NAME }}.dmg + + release: + if: contains(github.ref, 'tags/v') + runs-on: ubuntu-latest + needs: build_and_test + + steps: + - name: Get Artifacts + uses: actions/download-artifact@v4 + + - name: Create Release + uses: softprops/action-gh-release@v1 + with: + prerelease: true + # download-artifact puts these files in their own dirs... + # Using globs sidesteps having to pass the version around + files: | + */*.exe + */*.zip + */*.dmg diff --git a/.github/workflows/fire.yml b/.github/workflows/fire.yml deleted file mode 100644 index 1ee7b900..00000000 --- a/.github/workflows/fire.yml +++ /dev/null @@ -1,330 +0,0 @@ -# Continuous Integration for AU/VST3/Standalone audio plugins made with JUCE + CMake for Windows/Linux/macOS -# Adapted from github.com/cristianadam/HelloWorld - -name: CMake Build Matrix - -on: [ push, pull_request ] - -env: - PROJECT_NAME: Fire # Must match the JUCE project name from juce_add_plugin - CMAKE_VERSION: 3.16.2 - NINJA_VERSION: 1.9.0 - BUILD_TYPE: Release - CCACHE_VERSION: 3.7.7 - DISPLAY: :0 # linux pluginval needs this - -jobs: - build: - name: ${{ matrix.config.name }} - runs-on: ${{ matrix.config.os }} - strategy: - fail-fast: false - matrix: - config: - - { - name: "Windows Latest MSVC", - os: windows-2022, - cc: "cl", cxx: "cl", - environment_script: "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvars64.bat", - } - - { - name: "Ubuntu Latest GCC", - os: ubuntu-latest, - cc: "gcc", cxx: "g++" - } - - { - name: "macOS Latest Clang", - os: macos-latest, - cc: "clang", cxx: "clang++" - } - - steps: - - - uses: actions/checkout@v2 - with: - submodules: true - - - if: ${{ runner.os == 'Linux' }} - name: Install JUCE dependencies (Linux only) - id: juce_dependencies - run: | - sudo apt-get update - sudo apt-get install -y g++ libgtk-3-dev libfreetype6-dev libx11-dev libxinerama-dev libxrandr-dev libxcursor-dev mesa-common-dev libasound2-dev freeglut3-dev libxcomposite-dev libcurl4-openssl-dev libxext-dev libglu1-mesa-dev xvfb - sudo add-apt-repository -y ppa:webkit-team && sudo apt-get update - sudo apt-get install libwebkit2gtk-4.0-37 libwebkit2gtk-4.0-dev - - - name: Download Ninja and CMake - id: cmake_and_ninja - shell: cmake -P {0} - run: | - set(cmake_version $ENV{CMAKE_VERSION}) - set(ninja_version $ENV{NINJA_VERSION}) - message(STATUS "Using host CMake version: ${CMAKE_VERSION}") - if ("${{ runner.os }}" STREQUAL "Windows") - set(ninja_suffix "win.zip") - set(cmake_suffix "win64-x64.zip") - set(cmake_dir "cmake-${cmake_version}-win64-x64/bin") - elseif ("${{ runner.os }}" STREQUAL "Linux") - set(ninja_suffix "linux.zip") - set(cmake_suffix "Linux-x86_64.tar.gz") - set(cmake_dir "cmake-${cmake_version}-Linux-x86_64/bin") - elseif ("${{ runner.os }}" STREQUAL "macOS") - set(ninja_suffix "mac.zip") - set(cmake_suffix "Darwin-x86_64.tar.gz") - set(cmake_dir "cmake-${cmake_version}-Darwin-x86_64/CMake.app/Contents/bin") - endif() - set(ninja_url "https://github.com/ninja-build/ninja/releases/download/v${ninja_version}/ninja-${ninja_suffix}") - file(DOWNLOAD "${ninja_url}" ./ninja.zip SHOW_PROGRESS) - execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ./ninja.zip) - set(cmake_url "https://github.com/Kitware/CMake/releases/download/v${cmake_version}/cmake-${cmake_version}-${cmake_suffix}") - file(DOWNLOAD "${cmake_url}" ./cmake.zip SHOW_PROGRESS) - execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ./cmake.zip) - # Save the path for other steps - file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/${cmake_dir}" cmake_dir) - message("::set-output name=cmake_dir::${cmake_dir}") - if (NOT "${{ runner.os }}" STREQUAL "Windows") - execute_process( - COMMAND chmod +x ninja - COMMAND chmod +x ${cmake_dir}/cmake - ) - endif() - - - name: Download ccache - id: ccache - shell: cmake -P {0} - run: | - set(ccache_url "https://github.com/cristianadam/ccache/releases/download/v$ENV{CCACHE_VERSION}/${{ runner.os }}.tar.xz") - file(DOWNLOAD "${ccache_url}" ./ccache.tar.xz SHOW_PROGRESS) - execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ./ccache.tar.xz) - - - name: Prepare ccache timestamp - id: ccache_cache_timestamp - shell: cmake -P {0} - run: | - string(TIMESTAMP current_date "%Y-%m-%d-%H;%M;%S" UTC) - message("::set-output name=timestamp::${current_date}") - - - name: ccache cache files - uses: actions/cache@v1.1.0 - with: - path: .ccache - key: ${{ matrix.config.name }}-ccache-${{ steps.ccache_cache_timestamp.outputs.timestamp }} - restore-keys: | - ${{ matrix.config.name }}-ccache- - - - name: Configure - shell: cmake -P {0} - run: | - set(ENV{CC} ${{ matrix.config.cc }}) - set(ENV{CXX} ${{ matrix.config.cxx }}) - if ("${{ runner.os }}" STREQUAL "Windows" AND NOT "x${{ matrix.config.environment_script }}" STREQUAL "x") - execute_process( - COMMAND "${{ matrix.config.environment_script }}" && set - OUTPUT_FILE environment_script_output.txt - ) - file(STRINGS environment_script_output.txt output_lines) - foreach(line IN LISTS output_lines) - if (line MATCHES "^([a-zA-Z0-9_-]+)=(.*)$") - set(ENV{${CMAKE_MATCH_1}} "${CMAKE_MATCH_2}") - endif() - endforeach() - endif() - set(path_separator ":") - if ("${{ runner.os }}" STREQUAL "Windows") - set(path_separator ";") - endif() - set(ENV{PATH} "$ENV{GITHUB_WORKSPACE}${path_separator}$ENV{PATH}") - execute_process( - COMMAND ${{ steps.cmake_and_ninja.outputs.cmake_dir }}/cmake - -S . - -B build - -D CMAKE_BUILD_TYPE=$ENV{BUILD_TYPE} - -D CMAKE_C_COMPILER_LAUNCHER=ccache - -D CMAKE_CXX_COMPILER_LAUNCHER=ccache - RESULT_VARIABLE result - ) - if (NOT result EQUAL 0) - message(FATAL_ERROR "Bad exit status") - endif() - - - name: Build - shell: cmake -P {0} - run: | - set(ENV{NINJA_STATUS} "[%f/%t %o/sec] ") - if ("${{ runner.os }}" STREQUAL "Windows" AND NOT "x${{ matrix.config.environment_script }}" STREQUAL "x") - file(STRINGS environment_script_output.txt output_lines) - foreach(line IN LISTS output_lines) - if (line MATCHES "^([a-zA-Z0-9_-]+)=(.*)$") - set(ENV{${CMAKE_MATCH_1}} "${CMAKE_MATCH_2}") - endif() - endforeach() - endif() - set(path_separator ":") - if ("${{ runner.os }}" STREQUAL "Windows") - set(path_separator ";") - endif() - set(ENV{PATH} "$ENV{GITHUB_WORKSPACE}${path_separator}$ENV{PATH}") - file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}" ccache_basedir) - set(ENV{CCACHE_BASEDIR} "${ccache_basedir}") - set(ENV{CCACHE_DIR} "${ccache_basedir}/.ccache") - set(ENV{CCACHE_COMPRESS} "true") - set(ENV{CCACHE_COMPRESSLEVEL} "6") - set(ENV{CCACHE_MAXSIZE} "400M") - if ("${{ matrix.config.cxx }}" STREQUAL "cl") - set(ENV{CCACHE_MAXSIZE} "600M") - endif() - execute_process(COMMAND ccache -p) - execute_process(COMMAND ccache -z) - execute_process( - COMMAND ${{ steps.cmake_and_ninja.outputs.cmake_dir }}/cmake --build build --config "$ENV{BUILD_TYPE}" - RESULT_VARIABLE result - ) - if (NOT result EQUAL 0) - message(FATAL_ERROR "Bad exit status") - endif() - execute_process(COMMAND ccache -s) - - - name: Run tests - shell: cmake -P {0} - run: | - include(ProcessorCount) - ProcessorCount(N) - set(ENV{CTEST_OUTPUT_ON_FAILURE} "ON") - execute_process( - COMMAND ${{ steps.cmake_and_ninja.outputs.cmake_dir }}/ctest -j ${N} - WORKING_DIRECTORY build - RESULT_VARIABLE result - ) - if (NOT result EQUAL 0) - message(FATAL_ERROR "Running tests failed!") - endif() - - # - name: Pluginval Linux - # working-directory: build - # if: runner.os == 'Linux' - # run: | - # curl -L "https://github.com/Tracktion/pluginval/releases/download/v1.0.0-beta5/pluginval_Linux.zip" -o pluginval.zip - # unzip pluginval - # # start xvfb in the background - # sudo /usr/bin/Xvfb $DISPLAY -screen 0 1280x1024x24 & - # ./pluginval --strictness-level 5 --validate-in-process --verbose --output-dir "." --validate "${{ env.PROJECT_NAME }}_artefacts/${{ env.BUILD_TYPE }}/VST3/${{ env.PROJECT_NAME }}.vst3" - - - name: Pluginval Mac - working-directory: build - if: runner.os == 'MacOS' - run: | - curl -L "https://github.com/Tracktion/pluginval/releases/download/v1.0.0-beta5/pluginval_macOS.zip" -o pluginval.zip - unzip pluginval - pluginval.app/Contents/MacOS/pluginval --strictness-level 10 --validate-in-process --verbose --output-dir "." --validate "${{ env.PROJECT_NAME }}_artefacts/${{ env.BUILD_TYPE }}/VST3/${{ env.PROJECT_NAME }}.vst3" - - # - name: Pluginval Windows - # shell: cmd - # working-directory: build - # if: runner.os == 'Windows' - # run: | - # powershell -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; Invoke-WebRequest https://github.com/Tracktion/pluginval/releases/download/v1.0.0-beta5/pluginval_Windows.zip -OutFile pluginval.zip" - # powershell -Command "Expand-Archive pluginval.zip -DestinationPath ." - # pluginval.exe --strictness-level 10 --validate-in-process --output-dir "./bin" --validate "${{ env.PROJECT_NAME }}_artefacts/${{ env.BUILD_TYPE }}/VST3/${{ env.PROJECT_NAME }}.vst3" - # if %ERRORLEVEL% neq 0 { exit /b 1 } - - - if: ${{ runner.os=='Windows'}} - name: Pack (Windows) - run: | - mkdir ${{ github.event.repository.name }}-${{ runner.os }} - cd build\*_artefacts\Release - del *.* - move * ..\..\..\${{ github.event.repository.name }}-${{ runner.os }} - cd ..\..\.. - cmake -E tar cvf ${{ github.event.repository.name }}-${{ runner.os }}.zip --format=zip -- ${{ github.event.repository.name }}-${{ runner.os }} - - - if: ${{ runner.os!='Windows'}} - name: Pack (Unix) - run: | - mkdir ${{ github.event.repository.name }}-${{ runner.os }} - cd build/*_artefacts/Release - rm *.* - mv * ../../../${{ github.event.repository.name }}-${{ runner.os }} - cd ../../.. - cmake -E tar cvf ${{ github.event.repository.name }}-${{ runner.os }}.zip --format=zip -- ${{ github.event.repository.name }}-${{ runner.os }} - - - name: Upload - uses: actions/upload-artifact@v2 - with: - name: ${{ github.event.repository.name }}-${{ runner.os }} - path: ${{ github.event.repository.name }}-${{ runner.os }}.zip - - release: - if: contains(github.ref, 'tags/v') - runs-on: ubuntu-latest - needs: build - - steps: - - name: Create Release - id: create_release - uses: actions/create-release@v1.0.0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Release ${{ github.ref }} - draft: false - prerelease: false - - - name: Store Release url - run: | - echo "${{ steps.create_release.outputs.upload_url }}" > ./upload_url - - uses: actions/upload-artifact@v1 - with: - path: ./upload_url - name: upload_url - - publish: - if: contains(github.ref, 'tags/v') - name: Publish ${{ matrix.config.name }} - runs-on: ${{ matrix.config.os }} - strategy: - fail-fast: false - matrix: - config: - - { - name: "Windows Latest MSVC", artifact: "Windows", - os: ubuntu-latest - } - - { - name: "Ubuntu Latest GCC", artifact: "Linux", - os: ubuntu-latest - } - - { - name: "macOS Latest Clang", artifact: "macOS", - os: ubuntu-latest - } - needs: release - - steps: - - name: Download artifact - uses: actions/download-artifact@v1 - with: - name: ${{ github.event.repository.name }}-${{ matrix.config.artifact }} - path: ./ - - - name: Download URL - uses: actions/download-artifact@v1 - with: - name: upload_url - path: ./ - - - id: set_upload_url - run: | - upload_url=`cat ./upload_url` - echo ::set-output name=upload_url::$upload_url - - - name: Upload to Release - id: upload_to_release - uses: actions/upload-release-asset@v1.0.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.set_upload_url.outputs.upload_url }} - asset_path: ./${{ github.event.repository.name }}-${{ matrix.config.artifact }}.zip - asset_name: ${{ github.event.repository.name }}-${{ matrix.config.artifact }}.zip - asset_content_type: application/zip \ No newline at end of file diff --git a/.gitignore b/.gitignore index 46bf69d1..59645f05 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,30 @@ -# folder -Builds +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps **/.DS_Store + +# It's nice to have the Builds folder exist, to remind us where it is +Builds/* +!Builds/.gitkeep + +# This should never exist +Install/* + +# Running CTest makes a bunch o junk +Testing + +# IDE config +.idea + +# clion cmake builds +cmake-build-debug +cmake-build-release +cmake-build-relwithdebinfo +packaging/Output/* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index db85c57d..663ebaf1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,12 @@ [submodule "JUCE"] path = JUCE - url = https://github.com/juce-framework/JUCE + url = https://github.com/juce-framework/JUCE/ + branch = develop +[submodule "modules/melatonin_inspector"] + path = modules/melatonin_inspector + url = https://github.com/sudara/melatonin_inspector.git + branch = main +[submodule "cmake"] + path = cmake + url = https://github.com/sudara/cmake-includes.git + branch = main diff --git a/CMakeLists.txt b/CMakeLists.txt index d641ad0d..667756b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,202 +1,156 @@ -# Fire CMakeLists.txt - -# To get started on a new plugin, copy this entire folder (containing this file and C++ sources) to -# a convenient location, and then start making modifications. - -# The first line of any CMake project should be a call to `cmake_minimum_required`, which checks -# that the installed CMake will be able to understand the following CMakeLists, and ensures that -# CMake's behaviour is compatible with the named version. This is a standard CMake command, so more -# information can be found in the CMake docs. - -cmake_minimum_required(VERSION 3.15) - -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum OS X deployment version") - -# Building universals increases build times -# set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "") - -# The top-level CMakeLists.txt file for a project must contain a literal, direct call to the -# `project()` command. `project()` sets up some helpful variables that describe source/binary -# directories, and the current project version. This is a standard CMake command. - -project(Fire VERSION 1.0.0) - -# If you've installed JUCE somehow (via a package manager, or directly using the CMake install -# target), you'll need to tell this project that it depends on the installed copy of JUCE. If you've -# included JUCE directly in your source tree (perhaps as a submodule), you'll need to tell CMake to -# include that subdirectory as part of the build. - -# find_package(JUCE CONFIG REQUIRED) # If you've installed JUCE to your system -# or -# add_subdirectory(JUCE) # If you've put JUCE in a subdirectory called JUCE - -include(FetchContent) -FetchContent_Declare( - JUCE - GIT_REPOSITORY https://github.com/juce-framework/JUCE.git - GIT_TAG 7.0.1 +# 3.24.1 is bundled in Visual Studio 2022 v17.4 +# 3.24.1 is also bundled in CLion as of 2023 +cmake_minimum_required(VERSION 3.24.1) + +# This tells cmake we have goodies in the /cmake folder +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +include (PamplejuceVersion) + +# Configures universal binaries and decides which version of macOS to support +include(PamplejuceMacOS) + +# Change me! +# This is the internal name of the project and the name of JUCE's shared code target +# Note: This cannot have spaces (it may be 2024, but you can't have it all!) +# Worry not, JUCE's PRODUCT_NAME can have spaces (and is what DAWs display) +set(PROJECT_NAME "Fire") + +# Worry not, JUCE's PRODUCT_NAME can have spaces (and is what DAWs will display) +# You can also just have it be the same thing as PROJECT_NAME +# You may want to append the major version on the end of this (and PROJECT_NAME) ala: +# set(PROJECT_NAME "MyPlugin_v${MAJOR_VERSION}") +# Doing so enables major versions to show up in IDEs and DAWs as separate plugins +# allowing you to change parameters and behavior without breaking existing user projects +set(PRODUCT_NAME "Fire") + +# Change me! Used for the MacOS bundle name and Installers +set(COMPANY_NAME "Wings") + +# Change me! Used for the MacOS bundle identifier (and signing) +set(BUNDLE_ID "com.WingsDSP.Fire") + +# Change me! Set the plugin formats you want built +# Valid choices: AAX Unity VST VST3 AU AUv3 Standalone +set(FORMATS Standalone AU VST3 AUv3) + +# For simplicity, the name of the CMake project is also the name of the target +project(${PROJECT_NAME} VERSION ${CURRENT_VERSION}) + +# Couple tweaks that IMO should be JUCE defaults +include(JUCEDefaults) + +# JUCE is setup as a submodule in the /JUCE folder +# Locally, you must run `git submodule update --init --recursive` once +# and later `git submodule update --remote --merge` to keep it up to date +# On Github Actions, this is done as a part of actions/checkout +add_subdirectory(JUCE) + +# Add any other modules you want modules here, before the juce_add_plugin call +# juce_add_module(modules/my_module) + +# This adds the melatonin inspector module +add_subdirectory (modules/melatonin_inspector) + +# See `docs/CMake API.md` in the JUCE repo for all config options +juce_add_plugin("${PROJECT_NAME}" + # Icons for the standalone app + ICON_BIG "${CMAKE_CURRENT_SOURCE_DIR}/packaging/icon.png" + + # Change me! + COMPANY_NAME "${COMPANY_NAME}" + BUNDLE_ID "${BUNDLE_ID}" + + # On MacOS, plugin is copied to ~/Users/yourname/Library/Audio/Plug-Ins/ + COPY_PLUGIN_AFTER_BUILD TRUE + + # Change me! + # A four-character plugin id + # First character MUST be uppercase for AU formats + PLUGIN_MANUFACTURER_CODE Wing + + # Change me! + # A unique four-character plugin id + # Note: this must have at least one upper-case character + PLUGIN_CODE Fire + FORMATS "${FORMATS}" + + # The name of your final executable + # This is how it's listed in the DAW + # This can be different from PROJECT_NAME and can have spaces! + # You might want to use v${MAJOR_VERSION} here once you go to v2... + PRODUCT_NAME "${PRODUCT_NAME}") + +# This lets us use our code in both the JUCE targets and our Test target +# Without running into ODR violations +add_library(SharedCode INTERFACE) + +# C++20, please +# Use cxx_std_23 for C++23 (as of CMake v 3.20) +target_compile_features(SharedCode INTERFACE cxx_std_20) + +# Manually list all .h and .cpp files for the plugin +# If you are like me, you'll use globs for your sanity. +# Just ensure you employ CONFIGURE_DEPENDS so the build system picks up changes +# If you want to appease the CMake gods and avoid globs, manually add files like so: +# set(SourceFiles Source/PluginEditor.h Source/PluginProcessor.h Source/PluginEditor.cpp Source/PluginProcessor.cpp) +file(GLOB_RECURSE SourceFiles CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/source/*.h") +target_sources(SharedCode INTERFACE ${SourceFiles}) + +# Adds a BinaryData target for embedding assets into the binary +include(Assets) + +# MacOS only: Cleans up folder and target organization on Xcode. +include(XcodePrettify) + +# This is where you can set preprocessor definitions for JUCE and your plugin +target_compile_definitions(SharedCode + INTERFACE + + # JUCE_WEB_BROWSER and JUCE_USE_CURL off by default + JUCE_WEB_BROWSER=0 # If you set this to 1, add `NEEDS_WEB_BROWSER TRUE` to the `juce_add_plugin` call + JUCE_USE_CURL=0 # If you set this to 1, add `NEEDS_CURL TRUE` to the `juce_add_plugin` call + JUCE_VST3_CAN_REPLACE_VST2=0 + + # Uncomment if you are paying for a an Indie/Pro license or releasing under GPLv3 + # JUCE_DISPLAY_SPLASH_SCREEN=0 + + # lets the app known if we're Debug or Release + CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE}" + VERSION="${CURRENT_VERSION}" + + # JucePlugin_Name is for some reason doesn't use the nicer PRODUCT_NAME + PRODUCT_NAME_WITHOUT_VERSION="Fire" ) -FetchContent_MakeAvailable(JUCE) -FetchContent_Declare( - readerwriterqueue - GIT_REPOSITORY https://github.com/cameron314/readerwriterqueue - GIT_TAG v1.0.6 -) -FetchContent_MakeAvailable(readerwriterqueue) - -# If you are building a VST2 or AAX plugin, CMake needs to be told where to find these SDKs on your -# system. This setup should be done before calling `juce_add_plugin`. - -# juce_set_vst2_sdk_path(...) -# juce_set_aax_sdk_path(...) - -# `juce_add_plugin` adds a static library target with the name passed as the first argument -# (Fire here). This target is a normal CMake target, but has a lot of extra properties set -# up by default. As well as this shared code static library, this function adds targets for each of -# the formats specified by the FORMATS arguments. This function accepts many optional arguments. -# Check the readme at `docs/CMake API.md` in the JUCE repo for the full list. - -juce_add_plugin(Fire - # VERSION ... # Set this if the plugin version is different to the project version - # ICON_BIG ... # ICON_* arguments specify a path to an image file to use as an icon for the Standalone - # ICON_SMALL ... - COMPANY_NAME Wings # Specify the name of the plugin's author - IS_SYNTH FALSE # Is this a synth or an effect? - NEEDS_MIDI_INPUT TRUE # Does the plugin need midi input? - NEEDS_MIDI_OUTPUT FALSE # Does the plugin need midi output? - IS_MIDI_EFFECT FALSE # Is this plugin a MIDI effect? - # EDITOR_WANTS_KEYBOARD_FOCUS TRUE/FALSE # Does the editor need keyboard focus? - COPY_PLUGIN_AFTER_BUILD TRUE # Should the plugin be installed to a default location after building? - PLUGIN_MANUFACTURER_CODE Manu # A four-character manufacturer id with at least one upper-case character - PLUGIN_CODE Nykr # A unique four-character plugin id with exactly one upper-case character - # GarageBand 10.3 requires the first letter to be upper-case, and the remaining letters to be lower-case - FORMATS AU VST3 # The formats to build. Other valid formats are: AAX Unity VST AU AUv3 - PRODUCT_NAME "Fire") # The name of the final executable, which can differ from the target name - -target_link_libraries(Fire PUBLIC readerwriterqueue) -target_link_libraries(Fire PRIVATE juce::juce_dsp) - -target_compile_features(Fire PRIVATE cxx_std_20) - -# `juce_generate_juce_header` will create a JuceHeader.h for a given target, which will be generated -# into your build tree. This should be included with `#include `. The include path for -# this header will be automatically added to the target. The main function of the JuceHeader is to -# include all your JUCE module headers; if you're happy to include module headers directly, you -# probably don't need to call this. - -juce_generate_juce_header(Fire) - -# `target_sources` adds source files to a target. We pass the target that needs the sources as the -# first argument, then a visibility parameter for the sources which should normally be PRIVATE. -# Finally, we supply a list of source files that will be built into the target. This is a standard -# CMake command. - -target_sources(Fire - PRIVATE - Source/PluginEditor.cpp - Source/PluginProcessor.cpp - Source/DSP/WidthProcessor.cpp - Source/Panels/ControlPanel/Graph\ Components/DistortionGraph.cpp - Source/Panels/ControlPanel/Graph\ Components/GraphPanel.cpp - Source/Panels/ControlPanel/Graph\ Components/GraphTemplate.cpp - Source/Panels/ControlPanel/Graph\ Components/Oscilloscope.cpp - Source/Panels/ControlPanel/Graph\ Components/VUMeter.cpp - Source/Panels/ControlPanel/Graph\ Components/VUPanel.cpp - Source/Panels/ControlPanel/Graph\ Components/WidthGraph.cpp - Source/Panels/ControlPanel/BandPanel.cpp - Source/Panels/ControlPanel/GlobalPanel.cpp - Source/Panels/SpectrogramPanel/CloseButton.cpp - Source/Panels/SpectrogramPanel/DraggableButton.cpp - Source/Panels/SpectrogramPanel/EnableButton.cpp - Source/Panels/SpectrogramPanel/FilterControl.cpp - Source/Panels/SpectrogramPanel/FreqDividerGroup.cpp - Source/Panels/SpectrogramPanel/FreqTextLabel.cpp - Source/Panels/SpectrogramPanel/Multiband.cpp - Source/Panels/SpectrogramPanel/SoloButton.cpp - Source/Panels/SpectrogramPanel/SpectrumComponent.cpp - Source/Panels/SpectrogramPanel/VerticalLine.cpp - Source/Panels/TopPanel/Preset.cpp - Source/Utility/VersionInfo.cpp - ) - -# `target_compile_definitions` adds some preprocessor definitions to our target. In a Projucer -# project, these might be passed in the 'Preprocessor Definitions' field. JUCE modules also make use -# of compile definitions to switch certain features on/off, so if there's a particular feature you -# need that's not on by default, check the module header for the correct flag to set here. These -# definitions will be visible both to your code, and also the JUCE module code, so for new -# definitions, pick unique names that are unlikely to collide! This is a standard CMake command. - -target_compile_definitions(Fire - PUBLIC - # JUCE_WEB_BROWSER and JUCE_USE_CURL would be on by default, but you might not need them. - JUCE_WEB_BROWSER=0 # If you remove this, add `NEEDS_WEB_BROWSER TRUE` to the `juce_add_plugin` call - JUCE_USE_CURL=0 # If you remove this, add `NEEDS_CURL TRUE` to the `juce_add_plugin` call - JUCE_VST3_CAN_REPLACE_VST2=0) - -# If your target needs extra binary assets, you can add them here. The first argument is the name of -# a new static library target that will include all the binary resources. There is an optional -# `NAMESPACE` argument that can specify the namespace of the generated binary data class. Finally, -# the SOURCES argument should be followed by a list of source files that should be built into the -# static library. These source files can be of any kind (wav data, images, fonts, icons etc.). -# Conversion to binary-data will happen when your target is built. - -juce_add_binary_data(FireData SOURCES - resource/firelogo.png - resource/firewingslogo.png) - -# https://forum.juce.com/t/building-my-vst3-with-linux-and-juce6-couple-of-tiny-things/39014 -set_target_properties(FireData PROPERTIES POSITION_INDEPENDENT_CODE TRUE) - -# `target_link_libraries` links libraries and JUCE modules to other libraries or executables. Here, -# we're linking our executable target to the `juce::juce_audio_utils` module. Inter-module -# dependencies are resolved automatically, so `juce_core`, `juce_events` and so on will also be -# linked automatically. If we'd generated a binary data target above, we would need to link to it -# here too. This is a standard CMake command. - -target_link_libraries(Fire - PRIVATE - FireData - juce::juce_audio_utils - PUBLIC - juce::juce_recommended_config_flags - juce::juce_recommended_lto_flags - juce::juce_recommended_warning_flags) - -# BUILD_TESTING is ON by default -# include(CTest) does this too, but adds tons of targets we don't want -enable_testing() - -# All test .cpp files should be listed here -set(TestFiles - test/CatchMain.cpp - test/PluginTest.cpp) - -# Download the tagged version of Catch2 -Include(FetchContent) -FetchContent_Declare( - Catch2 - GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v2.13.7) -FetchContent_MakeAvailable(Catch2) - -# Setup the test executable, again C++ 20 please -add_executable(Tests ${TestFiles}) -target_compile_features(Tests PRIVATE cxx_std_20) - -# Pull in our plugin code for tests -target_include_directories(Tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) -target_link_libraries(Tests PRIVATE Catch2::Catch2 Fire FireData juce::juce_audio_utils) - -# Make an Xcode Scheme for the test executable so we can run tests in the IDE -set_target_properties(Tests PROPERTIES XCODE_GENERATE_SCHEME ON) - -# Organize the test source in the Tests/ folder in the IDE -source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/test PREFIX "" FILES ${TestFiles}) - -# Load and use the .cmake file provided by Catch2 -# https://github.com/catchorg/Catch2/blob/devel/docs/cmake-integration.md#parseandaddcatchtestscmake -# We have to manually provide the source directory here for now -# https://github.com/catchorg/Catch2/issues/2026 -include(${Catch2_SOURCE_DIR}/contrib/Catch.cmake) -catch_discover_tests(Tests) \ No newline at end of file +# Link to any other modules you added (with juce_add_module) here! +# Usually JUCE modules must have PRIVATE visibility +# See https://github.com/juce-framework/JUCE/blob/master/docs/CMake%20API.md#juce_add_module +# However, with Fire, you'll link modules to SharedCode with INTERFACE visibility +# This allows the JUCE plugin targets and the Tests target to link against it +target_link_libraries(SharedCode + INTERFACE + Assets + melatonin_inspector + juce_audio_utils + juce_audio_processors + juce_dsp + juce_gui_basics + juce_gui_extra + juce::juce_recommended_config_flags + juce::juce_recommended_lto_flags + juce::juce_recommended_warning_flags) + +# Link the JUCE plugin targets our SharedCode target +target_link_libraries("${PROJECT_NAME}" PRIVATE SharedCode) + +# IPP support, comment out to disable +include(PamplejuceIPP) + +# Everything related to the tests target +include(Tests) + +# A separate target keeps the Tests target fast! +include(Benchmarks) + +# Pass some config to GA (like our PRODUCT_NAME) +include(GitHubENV) diff --git a/Fire.jucer b/Fire.jucer index ceb9be7e..d55465b7 100644 --- a/Fire.jucer +++ b/Fire.jucer @@ -4,8 +4,16 @@ aaxIdentifier="com.WingsDSP.Fire" bundleIdentifier="com.WingsDSP.Fire" version="1.0.1" displaySplashScreen="1" pluginFormats="buildAU,buildVST3" jucerFormatVersion="1" pluginAUMainType="'aufx'" pluginVST3Category="Distortion" - addUsingNamespaceToJuceHeader="0" cppLanguageStandard="17" defines="JUCE_MODAL_LOOPS_PERMITTED=1"> + addUsingNamespaceToJuceHeader="0" cppLanguageStandard="17" defines="JUCE_MODAL_LOOPS_PERMITTED=1" + pluginManufacturerCode="Wing" pluginCode="Fire"> + + + + + + @@ -105,11 +113,6 @@ file="Source/PluginEditor.cpp"/> - - - - diff --git a/JuceLibraryCode/AppConfig.h b/JuceLibraryCode/AppConfig.h index dccc556d..e8ac08a8 100644 --- a/JuceLibraryCode/AppConfig.h +++ b/JuceLibraryCode/AppConfig.h @@ -45,7 +45,7 @@ #define JUCE_USE_DARK_SPLASH_SCREEN 1 -#define JUCE_PROJUCER_VERSION 0x70001 +#define JUCE_PROJUCER_VERSION 0x7000c //============================================================================== #define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 diff --git a/JuceLibraryCode/JuceHeader.h b/JuceLibraryCode/JuceHeader.h index c4b8f307..f18b6347 100644 --- a/JuceLibraryCode/JuceHeader.h +++ b/JuceLibraryCode/JuceHeader.h @@ -47,7 +47,7 @@ namespace ProjectInfo { const char* const projectName = "Fire"; const char* const companyName = ""; - const char* const versionString = "1.0.0.3"; - const int versionNumber = 0x1000003; + const char* const versionString = "1.0.1"; + const int versionNumber = 0x10001; } #endif diff --git a/JuceLibraryCode/JucePluginDefines.h b/JuceLibraryCode/JucePluginDefines.h index 3e56a9b8..c0a0acdd 100644 --- a/JuceLibraryCode/JucePluginDefines.h +++ b/JuceLibraryCode/JucePluginDefines.h @@ -56,10 +56,10 @@ #define JucePlugin_ManufacturerEmail "" #endif #ifndef JucePlugin_ManufacturerCode - #define JucePlugin_ManufacturerCode 0x4d616e75 + #define JucePlugin_ManufacturerCode 0x57696e67 #endif #ifndef JucePlugin_PluginCode - #define JucePlugin_PluginCode 0x4e796b72 + #define JucePlugin_PluginCode 0x46697265 #endif #ifndef JucePlugin_IsSynth #define JucePlugin_IsSynth 0 @@ -77,13 +77,13 @@ #define JucePlugin_EditorRequiresKeyboardFocus 0 #endif #ifndef JucePlugin_Version - #define JucePlugin_Version 1.0.0.3 + #define JucePlugin_Version 1.0.1 #endif #ifndef JucePlugin_VersionCode - #define JucePlugin_VersionCode 0x1000003 + #define JucePlugin_VersionCode 0x10001 #endif #ifndef JucePlugin_VersionString - #define JucePlugin_VersionString "1.0.0.3" + #define JucePlugin_VersionString "1.0.1" #endif #ifndef JucePlugin_VSTUniqueID #define JucePlugin_VSTUniqueID JucePlugin_PluginCode @@ -155,7 +155,7 @@ #define JucePlugin_ARAFactoryID "com.yourcompany.Fire.factory" #endif #ifndef JucePlugin_ARADocumentArchiveID - #define JucePlugin_ARADocumentArchiveID "com.yourcompany.Fire.aradocumentarchive.1.0.0" + #define JucePlugin_ARADocumentArchiveID "com.yourcompany.Fire.aradocumentarchive.1.0.1" #endif #ifndef JucePlugin_ARACompatibleArchiveIDs #define JucePlugin_ARACompatibleArchiveIDs "" diff --git a/JuceLibraryCode/include_juce_audio_plugin_client_VST_utils.mm b/JuceLibraryCode/include_juce_audio_plugin_client_AAX_utils.cpp similarity index 69% rename from JuceLibraryCode/include_juce_audio_plugin_client_VST_utils.mm rename to JuceLibraryCode/include_juce_audio_plugin_client_AAX_utils.cpp index 20951c26..d8864ab1 100644 --- a/JuceLibraryCode/include_juce_audio_plugin_client_VST_utils.mm +++ b/JuceLibraryCode/include_juce_audio_plugin_client_AAX_utils.cpp @@ -6,4 +6,4 @@ */ #include "AppConfig.h" -#include +#include diff --git a/JuceLibraryCode/include_juce_audio_plugin_client_AU.r b/JuceLibraryCode/include_juce_audio_plugin_client_VST2.mm similarity index 71% rename from JuceLibraryCode/include_juce_audio_plugin_client_AU.r rename to JuceLibraryCode/include_juce_audio_plugin_client_VST2.mm index 7b13a83b..011f3476 100644 --- a/JuceLibraryCode/include_juce_audio_plugin_client_AU.r +++ b/JuceLibraryCode/include_juce_audio_plugin_client_VST2.mm @@ -6,4 +6,4 @@ */ #include "AppConfig.h" -#include +#include diff --git a/JuceLibraryCode/include_juce_audio_plugin_client_utils.cpp b/JuceLibraryCode/include_juce_audio_plugin_client_VST3.mm similarity index 70% rename from JuceLibraryCode/include_juce_audio_plugin_client_utils.cpp rename to JuceLibraryCode/include_juce_audio_plugin_client_VST3.mm index 64167804..dccd9476 100644 --- a/JuceLibraryCode/include_juce_audio_plugin_client_utils.cpp +++ b/JuceLibraryCode/include_juce_audio_plugin_client_VST3.mm @@ -6,4 +6,4 @@ */ #include "AppConfig.h" -#include +#include diff --git a/Tests/PluginBasics.cpp b/Tests/PluginBasics.cpp new file mode 100644 index 00000000..77e387bb --- /dev/null +++ b/Tests/PluginBasics.cpp @@ -0,0 +1,35 @@ +#include "helpers/test_helpers.h" +#include +#include +#include + +TEST_CASE ("one is equal to one", "[dummy]") +{ + REQUIRE (1 == 1); +} + +TEST_CASE ("Plugin instance", "[instance]") +{ + PluginProcessor testPlugin; + + // This lets us use JUCE's MessageManager without leaking. + // PluginProcessor might need this if you use the APVTS for example. + // You'll also need it for tests that rely on juce::Graphics, juce::Timer, etc. + auto gui = juce::ScopedJuceInitialiser_GUI {}; + + SECTION ("name") + { + CHECK_THAT (testPlugin.getName().toStdString(), + Catch::Matchers::Equals ("Pamplejuce Demo")); + } +} + + +#ifdef PAMPLEJUCE_IPP + #include + +TEST_CASE ("IPP version", "[ipp]") +{ + CHECK_THAT (ippsGetLibVersion()->Version, Catch::Matchers::Equals ("2021.10.1 (r0x8e799c51)")); +} +#endif diff --git a/Tests/helpers/test_helpers.h b/Tests/helpers/test_helpers.h new file mode 100644 index 00000000..1292fc32 --- /dev/null +++ b/Tests/helpers/test_helpers.h @@ -0,0 +1,32 @@ +#pragma once +#include + +/* This is a helper function to run tests within the context of a plugin editor. + * + * Read more here: https://github.com/sudara/pamplejuce/issues/18#issuecomment-1425836807 + * + * Example usage (screenshots the plugin) + * + runWithinPluginEditor ([&] (PluginProcessor& plugin) { + auto snapshot = plugin.getActiveEditor()->createComponentSnapshot (plugin.getActiveEditor()->getLocalBounds(), true, 2.0f); + auto file = juce::File::getSpecialLocation (juce::File::SpecialLocationType::userDocumentsDirectory).getChildFile ("snapshot.jpeg"); + file.deleteFile(); + juce::FileOutputStream stream (file); + juce::JPEGImageFormat jpeg; + jpeg.writeImageToStream (snapshot, stream); + + REQUIRE (file.existsAsFile()); + }); + + */ +[[maybe_unused]] void runWithinPluginEditor (const std::function& testCode) +{ + PluginProcessor plugin; + auto gui = juce::ScopedJuceInitialiser_GUI {}; + auto editor = plugin.createEditorIfNeeded(); + + testCode (plugin); + + plugin.editorBeingDeleted (editor); + delete editor; +} diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..7f207341 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.1 \ No newline at end of file diff --git a/resource/firelogo.png b/assets/images/firelogo.png similarity index 100% rename from resource/firelogo.png rename to assets/images/firelogo.png diff --git a/assets/images/firelogo.psd b/assets/images/firelogo.psd new file mode 100644 index 00000000..553c82ac Binary files /dev/null and b/assets/images/firelogo.psd differ diff --git a/resource/firewingslogo.png b/assets/images/firewingslogo.png similarity index 100% rename from resource/firewingslogo.png rename to assets/images/firewingslogo.png diff --git a/assets/images/firewingslogo.psd b/assets/images/firewingslogo.psd new file mode 100644 index 00000000..ec3517bd Binary files /dev/null and b/assets/images/firewingslogo.psd differ diff --git a/assets/images/pamplejuce.png b/assets/images/pamplejuce.png new file mode 100644 index 00000000..3d573e0f Binary files /dev/null and b/assets/images/pamplejuce.png differ diff --git a/benchmarks/Benchmarks.cpp b/benchmarks/Benchmarks.cpp new file mode 100644 index 00000000..17b90188 --- /dev/null +++ b/benchmarks/Benchmarks.cpp @@ -0,0 +1,40 @@ +#include "PluginEditor.h" +#include "catch2/benchmark/catch_benchmark_all.hpp" +#include "catch2/catch_test_macros.hpp" + +TEST_CASE ("Boot performance") +{ + BENCHMARK_ADVANCED ("Processor constructor") + (Catch::Benchmark::Chronometer meter) + { + auto gui = juce::ScopedJuceInitialiser_GUI {}; + std::vector> storage (size_t (meter.runs())); + meter.measure ([&] (int i) { storage[(size_t) i].construct(); }); + }; + + BENCHMARK_ADVANCED ("Processor destructor") + (Catch::Benchmark::Chronometer meter) + { + auto gui = juce::ScopedJuceInitialiser_GUI {}; + std::vector> storage (size_t (meter.runs())); + for (auto& s : storage) + s.construct(); + meter.measure ([&] (int i) { storage[(size_t) i].destruct(); }); + }; + + BENCHMARK_ADVANCED ("Editor open and close") + (Catch::Benchmark::Chronometer meter) + { + auto gui = juce::ScopedJuceInitialiser_GUI {}; + + PluginProcessor plugin; + + // due to complex construction logic of the editor, let's measure open/close together + meter.measure ([&] (int /* i */) { + auto editor = plugin.createEditorIfNeeded(); + plugin.editorBeingDeleted (editor); + delete editor; + return plugin.getActiveEditor(); + }); + }; +} diff --git a/cmake/Assets.cmake b/cmake/Assets.cmake new file mode 100644 index 00000000..52a51edc --- /dev/null +++ b/cmake/Assets.cmake @@ -0,0 +1,11 @@ +# HEADS UP: Pamplejuce assumes anything you stick in the assets folder you want to included in your binary! +# This makes life easy, but will bloat your binary needlessly if you include unused files +file(GLOB_RECURSE AssetFiles CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/assets/*") +list (FILTER AssetFiles EXCLUDE REGEX "/\\.DS_Store$") # We don't want the .DS_Store on macOS though... + +# Setup our binary data as a target called Assets +juce_add_binary_data(Assets SOURCES ${AssetFiles}) + +# Required for Linux happiness: +# See https://forum.juce.com/t/loading-pytorch-model-using-binarydata/39997/2 +set_target_properties(Assets PROPERTIES POSITION_INDEPENDENT_CODE TRUE) diff --git a/cmake/Benchmarks.cmake b/cmake/Benchmarks.cmake new file mode 100644 index 00000000..18f87148 --- /dev/null +++ b/cmake/Benchmarks.cmake @@ -0,0 +1,26 @@ +file(GLOB_RECURSE BenchmarkFiles CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/benchmarks/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/Benchmarks/*.h") + +# Organize the test source in the Tests/ folder in the IDE +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/benchmarks PREFIX "" FILES ${BenchmarkFiles}) + +add_executable(Benchmarks ${BenchmarkFiles}) +target_compile_features(Benchmarks PRIVATE cxx_std_20) +catch_discover_tests(Benchmarks) + +# Our benchmark executable also wants to know about our plugin code... +target_include_directories(Benchmarks PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/source) + +# Copy over compile definitions from our plugin target so it has all the JUCEy goodness +target_compile_definitions(Benchmarks PRIVATE $) + +# And give tests access to our shared code +target_link_libraries(Benchmarks PRIVATE SharedCode Catch2::Catch2WithMain) + +# Make an Xcode Scheme for the test executable so we can run tests in the IDE +set_target_properties(Benchmarks PROPERTIES XCODE_GENERATE_SCHEME ON) + +# When running Tests we have specific needs +target_compile_definitions(Benchmarks PUBLIC + JUCE_MODAL_LOOPS_PERMITTED=1 # let us run Message Manager in tests + RUN_PAMPLEJUCE_TESTS=1 # also run tests in module .cpp files guarded by RUN_PAMPLEJUCE_TESTS +) diff --git a/cmake/CHANGELOG.md b/cmake/CHANGELOG.md new file mode 100644 index 00000000..1b5f8850 --- /dev/null +++ b/cmake/CHANGELOG.md @@ -0,0 +1,8 @@ +## 2023-09-04 + +* Added `SharedCodeDefaults.cmake` which handles setting C++20 and fast math on the `SharedCode` Target. +* Modified CTest to report on failure + +## 2023-09-04 + +Initial commit. Added this CHANGELOG. diff --git a/cmake/GitHubENV.cmake b/cmake/GitHubENV.cmake new file mode 100644 index 00000000..5efac9f6 --- /dev/null +++ b/cmake/GitHubENV.cmake @@ -0,0 +1,13 @@ +# Write some temp files to make GitHub Actions / packaging easy + +if ((DEFINED ENV{CI})) + set (env_file "${PROJECT_SOURCE_DIR}/.env") + message ("Writing ENV file for CI: ${env_file}") + + # the first call truncates, the rest append + file(WRITE "${env_file}" "PROJECT_NAME=${PROJECT_NAME}\n") + file(APPEND "${env_file}" "PRODUCT_NAME=${PRODUCT_NAME}\n") + file(APPEND "${env_file}" "VERSION=${CURRENT_VERSION}\n") + file(APPEND "${env_file}" "BUNDLE_ID=${BUNDLE_ID}\n") + file(APPEND "${env_file}" "COMPANY_NAME=${COMPANY_NAME}\n") +endif () diff --git a/cmake/JUCEDefaults.cmake b/cmake/JUCEDefaults.cmake new file mode 100644 index 00000000..07241e24 --- /dev/null +++ b/cmake/JUCEDefaults.cmake @@ -0,0 +1,14 @@ +# Adds all the module sources so they appear correctly in the IDE +# Must be set before JUCE is added as a sub-dir (or any targets are made) +# https://github.com/juce-framework/JUCE/commit/6b1b4cf7f6b1008db44411f2c8887d71a3348889 +set_property(GLOBAL PROPERTY USE_FOLDERS YES) + +# Creates a /Modules directory in the IDE with the JUCE Module code +option(JUCE_ENABLE_MODULE_SOURCE_GROUPS "Show all module sources in IDE projects" ON) + +# Color our warnings and errors +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + add_compile_options(-fdiagnostics-color=always) +elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + add_compile_options(-fcolor-diagnostics) +endif () diff --git a/cmake/PamplejuceIPP.cmake b/cmake/PamplejuceIPP.cmake new file mode 100644 index 00000000..40d08a04 --- /dev/null +++ b/cmake/PamplejuceIPP.cmake @@ -0,0 +1,11 @@ +# When present, use Intel IPP for performance on Windows +if (WIN32) # Can't use MSVC here, as it won't catch Clang on Windows + find_package(IPP) + if (IPP_FOUND) + target_link_libraries(SharedCode INTERFACE IPP::ipps IPP::ippcore IPP::ippi IPP::ippcv) + message("IPP LIBRARIES FOUND") + target_compile_definitions(SharedCode INTERFACE PAMPLEJUCE_IPP=1) + else () + message("IPP LIBRARIES *NOT* FOUND") + endif () +endif () diff --git a/cmake/PamplejuceMacOS.cmake b/cmake/PamplejuceMacOS.cmake new file mode 100644 index 00000000..6cc13693 --- /dev/null +++ b/cmake/PamplejuceMacOS.cmake @@ -0,0 +1,15 @@ + +# This must be set before the project() call +# see: https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_DEPLOYMENT_TARGET.html +# FORCE must be set, see https://stackoverflow.com/a/44340246 +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Support macOS down to High Sierra" FORCE) + +# Building universal binaries on macOS increases build time +# This is set on CI but not during local dev +if ((DEFINED ENV{CI}) AND (CMAKE_BUILD_TYPE STREQUAL "Release")) + message("Building for Apple Silicon and x86_64") + set(CMAKE_OSX_ARCHITECTURES arm64 x86_64) +endif () + +# By default we don't want Xcode schemes to be made for modules, etc +set(CMAKE_XCODE_GENERATE_SCHEME OFF) diff --git a/cmake/PamplejuceVersion.cmake b/cmake/PamplejuceVersion.cmake new file mode 100644 index 00000000..cc337592 --- /dev/null +++ b/cmake/PamplejuceVersion.cmake @@ -0,0 +1,9 @@ +# Reads in our VERSION file and sticks in it CURRENT_VERSION variable +# Be sure the file has no newlines! +# This exposes CURRENT_VERSION to the build system +# And it's later fed to JUCE so it shows up as VERSION in your IDE +file(STRINGS VERSION CURRENT_VERSION) + +# Figure out the major version to append to our PROJECT_NAME +string(REGEX MATCH "([0-9]+)" MAJOR_VERSION ${CURRENT_VERSION}) +message(STATUS "Major version: ${MAJOR_VERSION}") diff --git a/cmake/README.md b/cmake/README.md new file mode 100644 index 00000000..dce974c4 --- /dev/null +++ b/cmake/README.md @@ -0,0 +1,33 @@ +# CMake Includes for Pamplejuce + +Hi there! + +## What is this? + +It's most of the actual CMake functionality used by [Pamplejuce](https://github.com/sudara/pamplejuce), my template repository for plugins in the JUCE framework. + +## Why is this its own git submodule? + +It's to help projects built by the template pull in the lastest changes. + +[Pamplejuce](https://github.com/sudara/pamplejuce) is a template repository. Unlike most "dependencies," when you hit "Create Template" you are literally copying and pasting the code. Which sorta sucks, as people can't get fixes or updates. + +## Why would I want updates? + +For at least the gritty CMake details, there are fixes, improvements and additional functionality being added. + +In the best case, as a submodule, you can pull in the fixes and improvements. + +In the worst case, this seperate repo will help you see what exactly changed in Pamplejuce. + +## Is it risky? + +It could be! + +As of 2023, Pamplejuce is still being changed around a bunch, with the goal of being a better and better ecosystem for developers. + +That means there could be breakage when you pull. + +## What changed recently tho? + +See [CHANGELOG.md](CHANGELOG.md). diff --git a/cmake/SharedCodeDefaults.cmake b/cmake/SharedCodeDefaults.cmake new file mode 100644 index 00000000..a3f8afdc --- /dev/null +++ b/cmake/SharedCodeDefaults.cmake @@ -0,0 +1,19 @@ +# fast math and better simd support in RELEASE and RELWITHDEBINFO +if (MSVC) + # https://learn.microsoft.com/en-us/cpp/build/reference/fp-specify-floating-point-behavior?view=msvc-170#fast + target_compile_options(SharedCode INTERFACE $<$:/fp:fast>) +else () + # See the implications here: + # https://stackoverflow.com/q/45685487 + target_compile_options(SharedCode INTERFACE $<$:-Ofast>) + target_compile_options(SharedCode INTERFACE $<$:-Ofast>) +endif () + +# Tell MSVC to properly report what c++ version is being used +if (MSVC) + target_compile_options(SharedCode INTERFACE /Zc:__cplusplus) +endif () + +# C++20, please +# Use cxx_std_23 for C++23 (as of CMake v 3.20) +target_compile_features(SharedCode INTERFACE cxx_std_20) diff --git a/cmake/Tests.cmake b/cmake/Tests.cmake new file mode 100644 index 00000000..1cd491e1 --- /dev/null +++ b/cmake/Tests.cmake @@ -0,0 +1,55 @@ +# Required for ctest (which is just an easier way to run in cross-platform CI) +# include(CTest) could be used too, but adds additional targets we don't care about +# See: https://github.com/catchorg/Catch2/issues/2026 +# You can also forgo ctest entirely and call ./Tests directly from the build dir +enable_testing() + +# Go into detail when there's a CTest failure +set(CTEST_OUTPUT_ON_FAILURE ON) +set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) + +# "GLOBS ARE BAD" is brittle and silly dev UX, sorry CMake! +file(GLOB_RECURSE TestFiles CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/tests/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/tests/*.h") + +# Organize the test source in the Tests/ folder in Xcode +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/tests PREFIX "" FILES ${TestFiles}) + +# Use Catch2 v3 on the devel branch +Include(FetchContent) +FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_PROGRESS TRUE + GIT_SHALLOW TRUE + GIT_TAG v3.4.0) +FetchContent_MakeAvailable(Catch2) # find_package equivalent + +# Setup the test executable, again C++20 please +add_executable(Tests ${TestFiles}) +target_compile_features(Tests PRIVATE cxx_std_20) + +# Our test executable also wants to know about our plugin code... +target_include_directories(Tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/source) + +# Copy over compile definitions from our plugin target so it has all the JUCEy goodness +target_compile_definitions(Tests PRIVATE $) + +# And give tests access to our shared code +target_link_libraries(Tests PRIVATE SharedCode Catch2::Catch2WithMain) + +# Make an Xcode Scheme for the test executable so we can run tests in the IDE +set_target_properties(Tests PROPERTIES XCODE_GENERATE_SCHEME ON) + +# When running Tests we have specific needs +target_compile_definitions(Tests PUBLIC + JUCE_MODAL_LOOPS_PERMITTED=1 # let us run Message Manager in tests + RUN_PAMPLEJUCE_TESTS=1 # also run tests in other module .cpp files guarded by RUN_PAMPLEJUCE_TESTS +) + +# Load and use the .cmake file provided by Catch2 +# https://github.com/catchorg/Catch2/blob/devel/docs/cmake-integration.md +# We have to manually provide the source directory here for now +include(${Catch2_SOURCE_DIR}/extras/Catch.cmake) +# ${DISCOVERY_MODE} set to "PRE_TEST" for MacOS arm64 / Xcode development +# fixes error when Xcode attempts to run test executable +catch_discover_tests(Tests ${DISCOVERY_MODE} "PRE_TEST") diff --git a/cmake/XcodePrettify.cmake b/cmake/XcodePrettify.cmake new file mode 100644 index 00000000..c6ec34ac --- /dev/null +++ b/cmake/XcodePrettify.cmake @@ -0,0 +1,31 @@ +# No, we don't want our source buried in extra nested folders +set_target_properties(SharedCode PROPERTIES FOLDER "") + +# The Xcode source tree should uhhh, still look like the source tree, yo +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/source PREFIX "" FILES ${SourceFiles}) + +# It tucks the Plugin varieties into a "Targets" folder and generate an Xcode Scheme manually +# Xcode scheme generation is turned off globally to limit noise from other targets +# The non-hacky way of doing this is via the global PREDEFINED_TARGETS_FOLDER property +# However that doesn't seem to be working in Xcode +# Not all plugin types (au, vst) available on each build type (win, macos, linux) +foreach (target ${FORMATS} "All") + if (TARGET ${PROJECT_NAME}_${target}) + set_target_properties(${PROJECT_NAME}_${target} PROPERTIES + # Tuck the actual plugin targets into a folder where they won't bother us + FOLDER "Targets" + # Let us build the target in Xcode + XCODE_GENERATE_SCHEME ON) + + # Set the default executable that Xcode will open on build + # Note: you must manually build the AudioPluginHost.xcodeproj in the JUCE subdir + if ((NOT target STREQUAL "All") AND (NOT target STREQUAL "Standalone")) + set_target_properties(${PROJECT_NAME}_${target} PROPERTIES + XCODE_SCHEME_EXECUTABLE "${CMAKE_CURRENT_SOURCE_DIR}/JUCE/extras/AudioPluginHost/Builds/MacOSX/build/Debug/AudioPluginHost.app") + endif () + endif () +endforeach () + +if (TARGET Assets) + set_target_properties(Assets PROPERTIES FOLDER "Targets") +endif () diff --git a/modules/.gitkeep b/modules/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/modules/melatonin_inspector/.clang-format b/modules/melatonin_inspector/.clang-format new file mode 100644 index 00000000..2157c3bc --- /dev/null +++ b/modules/melatonin_inspector/.clang-format @@ -0,0 +1,88 @@ +# https://forum.juce.com/t/automatic-juce-like-code-formatting-with-clang-format/31624/20 +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BreakAfterJavaFieldAnnotations: false +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BraceWrapping: # Allman except for lambdas + AfterClass: true + AfterCaseLabel: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + BeforeElse: true + AfterControlStatement: Always + BeforeLambdaBody: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakStringLiterals: false +ColumnLimit: 0 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +IndentCaseLabels: true +IndentPPDirectives: BeforeHash +IndentWidth: 4 +IndentWrappedFunctionNames: true +KeepEmptyLinesAtTheStartOfBlocks: false +Language: Cpp +MaxEmptyLinesToKeep: 1 +FixNamespaceComments: false +NamespaceIndentation: All +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: true +SpaceBeforeParens: NonEmptyParentheses +SpaceInEmptyParentheses: false +SpaceBeforeInheritanceColon: true +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: true +SpacesInParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 +SpacesInSquareBrackets: false +Standard: "c++20" +TabWidth: 4 +UseTab: Never +UseCRLF: false +--- +Language: ObjC +BasedOnStyle: Chromium +BreakBeforeBraces: Allman +ColumnLimit: 0 +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Left +SpacesBeforeTrailingComments: 1 +TabWidth: 4 +UseTab: Never diff --git a/modules/melatonin_inspector/.gitattributes b/modules/melatonin_inspector/.gitattributes new file mode 100644 index 00000000..176a458f --- /dev/null +++ b/modules/melatonin_inspector/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/modules/melatonin_inspector/.github/FUNDING.yml b/modules/melatonin_inspector/.github/FUNDING.yml new file mode 100644 index 00000000..f63f791f --- /dev/null +++ b/modules/melatonin_inspector/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: sudara diff --git a/modules/melatonin_inspector/.github/workflows/ci.yml b/modules/melatonin_inspector/.github/workflows/ci.yml new file mode 100644 index 00000000..2774b5b1 --- /dev/null +++ b/modules/melatonin_inspector/.github/workflows/ci.yml @@ -0,0 +1,81 @@ +name: CI + +on: + workflow_dispatch: + push: + pull_request: + branches: + - main + - next + +env: + CMAKE_BUILD_PARALLEL_LEVEL: 3 # Use up to 3 cpus to build juceaide, etc + +concurrency: + group: ${{ github.workflow }}.${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +permissions: + contents: read + +jobs: + + BuildAndTest: + name: ${{ matrix.os }} ${{ matrix.app }} + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + + strategy: + fail-fast: false + matrix: + app: [ member_enabled, member_disabled, unique_ptr_enabled, unique_ptr_disabled ] + os: [ macos-latest, windows-latest, ubuntu-latest ] + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 1 + + - name: Install Linux Deps + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt install libasound2-dev libx11-dev libxcomposite-dev libxcursor-dev libxext-dev libxinerama-dev libxrandr-dev libxrender-dev libfreetype6-dev libglu1-mesa-dev libjack-jackd2-dev + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 9 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 9 + + + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.3 + + - name: Configure + run: cmake -B Builds -DTARGET_NAME:STRING=${{ matrix.app }} + + - name: Build + run: cmake --build Builds --parallel 4 + + - name: Run + if: ${{ matrix.os == 'macos-latest' }} + working-directory: Builds/${{ matrix.app }}_artefacts + timeout-minutes: 2 + run: | + ls -ahl + ${{ matrix.app }}.app/Contents/MacOS/${{ matrix.app }} & + + - name: Run + if: ${{ matrix.os == 'windows-latest' }} + working-directory: Builds/${{ matrix.app }}_artefacts + timeout-minutes: 2 + run: | + ls -ahl + ./Debug/${{ matrix.app }}.exe & + + - uses: OrbitalOwen/desktop-screenshot-action@0.1 + if: ${{ matrix.os == 'windows-latest' }} + with: + file-name: ${{ matrix.os }}-${{matrix.app}}.jpg diff --git a/modules/melatonin_inspector/.gitignore b/modules/melatonin_inspector/.gitignore new file mode 100644 index 00000000..dad2f985 --- /dev/null +++ b/modules/melatonin_inspector/.gitignore @@ -0,0 +1,29 @@ +.DS_Store + +# build directories +[Bb]uilds/ +[Bb]uild/ + +# other directories generated by various tools +Testing/ +[Cc]ache/ +.cache/ +[Dd]eploy/ + +# IDE-specific files and directories +xcuserdata/ +.idea/ +cmake-build-*/ +[Oo]ut/ +.vs/ +.vscode/ +.history/ +*.vim +[._]*.un~ +*.sublime-workspace + +# other files +.[Tt]rash* +.nfs* +*.bak +*.tmp diff --git a/modules/melatonin_inspector/Assets/Clear.png b/modules/melatonin_inspector/Assets/Clear.png new file mode 100644 index 00000000..acfc11a9 Binary files /dev/null and b/modules/melatonin_inspector/Assets/Clear.png differ diff --git a/modules/melatonin_inspector/Assets/Eyedropper-off.png b/modules/melatonin_inspector/Assets/Eyedropper-off.png new file mode 100644 index 00000000..c6999331 Binary files /dev/null and b/modules/melatonin_inspector/Assets/Eyedropper-off.png differ diff --git a/modules/melatonin_inspector/Assets/Eyedropper-on.png b/modules/melatonin_inspector/Assets/Eyedropper-on.png new file mode 100644 index 00000000..8f076330 Binary files /dev/null and b/modules/melatonin_inspector/Assets/Eyedropper-on.png differ diff --git a/modules/melatonin_inspector/Assets/Logo.png b/modules/melatonin_inspector/Assets/Logo.png new file mode 100644 index 00000000..5dc5b265 Binary files /dev/null and b/modules/melatonin_inspector/Assets/Logo.png differ diff --git a/modules/melatonin_inspector/Assets/Search.png b/modules/melatonin_inspector/Assets/Search.png new file mode 100644 index 00000000..cbb3a3d1 Binary files /dev/null and b/modules/melatonin_inspector/Assets/Search.png differ diff --git a/modules/melatonin_inspector/Assets/dogfood-off.png b/modules/melatonin_inspector/Assets/dogfood-off.png new file mode 100644 index 00000000..836232b9 Binary files /dev/null and b/modules/melatonin_inspector/Assets/dogfood-off.png differ diff --git a/modules/melatonin_inspector/Assets/dogfood-on.png b/modules/melatonin_inspector/Assets/dogfood-on.png new file mode 100644 index 00000000..f23e4437 Binary files /dev/null and b/modules/melatonin_inspector/Assets/dogfood-on.png differ diff --git a/modules/melatonin_inspector/Assets/enabled-off.png b/modules/melatonin_inspector/Assets/enabled-off.png new file mode 100644 index 00000000..4a707eb6 Binary files /dev/null and b/modules/melatonin_inspector/Assets/enabled-off.png differ diff --git a/modules/melatonin_inspector/Assets/enabled-on.png b/modules/melatonin_inspector/Assets/enabled-on.png new file mode 100644 index 00000000..f971f61e Binary files /dev/null and b/modules/melatonin_inspector/Assets/enabled-on.png differ diff --git a/modules/melatonin_inspector/Assets/expand-off.png b/modules/melatonin_inspector/Assets/expand-off.png new file mode 100644 index 00000000..87448544 Binary files /dev/null and b/modules/melatonin_inspector/Assets/expand-off.png differ diff --git a/modules/melatonin_inspector/Assets/expand-on.png b/modules/melatonin_inspector/Assets/expand-on.png new file mode 100644 index 00000000..14be5950 Binary files /dev/null and b/modules/melatonin_inspector/Assets/expand-on.png differ diff --git a/modules/melatonin_inspector/Assets/move-off.png b/modules/melatonin_inspector/Assets/move-off.png new file mode 100644 index 00000000..ee605363 Binary files /dev/null and b/modules/melatonin_inspector/Assets/move-off.png differ diff --git a/modules/melatonin_inspector/Assets/move-on.png b/modules/melatonin_inspector/Assets/move-on.png new file mode 100644 index 00000000..7ca7df40 Binary files /dev/null and b/modules/melatonin_inspector/Assets/move-on.png differ diff --git a/modules/melatonin_inspector/Assets/speedometer-off.png b/modules/melatonin_inspector/Assets/speedometer-off.png new file mode 100644 index 00000000..12da163c Binary files /dev/null and b/modules/melatonin_inspector/Assets/speedometer-off.png differ diff --git a/modules/melatonin_inspector/Assets/speedometer-on.png b/modules/melatonin_inspector/Assets/speedometer-on.png new file mode 100644 index 00000000..7a67a629 Binary files /dev/null and b/modules/melatonin_inspector/Assets/speedometer-on.png differ diff --git a/modules/melatonin_inspector/Assets/tab-off.png b/modules/melatonin_inspector/Assets/tab-off.png new file mode 100644 index 00000000..8a202b77 Binary files /dev/null and b/modules/melatonin_inspector/Assets/tab-off.png differ diff --git a/modules/melatonin_inspector/Assets/tab-on.png b/modules/melatonin_inspector/Assets/tab-on.png new file mode 100644 index 00000000..81225b3f Binary files /dev/null and b/modules/melatonin_inspector/Assets/tab-on.png differ diff --git a/modules/melatonin_inspector/Assets/timing-off.png b/modules/melatonin_inspector/Assets/timing-off.png new file mode 100644 index 00000000..0ad6b558 Binary files /dev/null and b/modules/melatonin_inspector/Assets/timing-off.png differ diff --git a/modules/melatonin_inspector/Assets/timing-on.png b/modules/melatonin_inspector/Assets/timing-on.png new file mode 100644 index 00000000..6b18fd18 Binary files /dev/null and b/modules/melatonin_inspector/Assets/timing-on.png differ diff --git a/modules/melatonin_inspector/CMakeLists.txt b/modules/melatonin_inspector/CMakeLists.txt new file mode 100644 index 00000000..1397f6f4 --- /dev/null +++ b/modules/melatonin_inspector/CMakeLists.txt @@ -0,0 +1,64 @@ +cmake_minimum_required(VERSION 3.20) + +project(MelatoninInspector VERSION 1.3.0 LANGUAGES CXX + DESCRIPTION "JUCE module for inspecting Components" + HOMEPAGE_URL "https://github.com/sudara/melatonin_inspector") + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED YES) + +include(FetchContent) +if (MelatoninInspector_IS_TOP_LEVEL) + message(STATUS "Cloning JUCE...") + + FetchContent_Declare(JUCE + GIT_REPOSITORY https://github.com/juce-framework/JUCE.git + GIT_TAG origin/master + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE + FIND_PACKAGE_ARGS 7.0.6) + + FetchContent_MakeAvailable(JUCE) + + SET(TARGET_NAME "unique_ptr_disabled" CACHE STRING "Name of cpp and target to compile") + + juce_add_gui_app ("${TARGET_NAME}" VERSION 1.0.0) + target_sources("${TARGET_NAME}" PRIVATE "tests/${TARGET_NAME}.cpp") + + target_compile_definitions("${TARGET_NAME}" PUBLIC + JUCE_USE_CURL=0 + JUCE_WEB_BROWSER=0 + ) + + set_target_properties("${TARGET_NAME}" PROPERTIES COMPILE_WARNING_AS_ERROR ON) + +endif () + +if (NOT COMMAND juce_add_module) + message(FATAL_ERROR "JUCE must be added to your project before melatonin_inspector!") +endif () + +# this makes the assumption the current directory is named melatonin_inspector +juce_add_module("${CMAKE_CURRENT_LIST_DIR}") +add_library(Melatonin::Inspector ALIAS melatonin_inspector) + +if (MelatoninInspector_IS_TOP_LEVEL) + target_link_libraries("${TARGET_NAME}" PRIVATE melatonin_inspector + juce::juce_recommended_config_flags + juce::juce_recommended_lto_flags + juce::juce_recommended_warning_flags) +endif () + +# Assets are precompiled in the module to make it Projucer friendly +# +# To add or modify images: +# * Uncomment the following CMake lines to generate the MelatoninInspectorAssets binary +# * uncomment #include "InspectorBinaryData.h in misc.h and comment out the LatestCompiledAssets include +# * build as you would normally via CMake +# * run ./modules/melatonin_inspector/copy_cmake_assets.rb from your root PROJECT folder +# * comment these lines back out and swap the misc.h include again + +#file(GLOB_RECURSE MelatoninInspectorAssetFiles CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/Assets/*") +#juce_add_binary_data(MelatoninInspectorAssets SOURCES ${MelatoninInspectorAssetFiles} HEADER_NAME InspectorBinaryData.h NAMESPACE InspectorBinaryData) +#set_target_properties(MelatoninInspectorAssets PROPERTIES POSITION_INDEPENDENT_CODE TRUE) +#target_link_libraries(melatonin_inspector INTERFACE MelatoninInspectorAssets) diff --git a/modules/melatonin_inspector/LICENSE b/modules/melatonin_inspector/LICENSE new file mode 100644 index 00000000..d083ea21 --- /dev/null +++ b/modules/melatonin_inspector/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Sudara Williams + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData1.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData1.cpp new file mode 100644 index 00000000..32745275 --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData1.cpp @@ -0,0 +1,172 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== .DS_Store ================== +static const unsigned char temp_binary_data_0[] = +{ 0,0,0,1,66,117,100,49,0,0,16,0,0,0,8,0,0,0,16,0,0,0,4,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,4,0,0,0,1,0,0,16,0,0,114,0,46,0,112,0,110,0,103,112,116,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,9,0,99,0,108,0,101,0,97,0,114,0,46,0,112,0,110,0,103,112,116,98,76,117,115,116,114,0,0,0,89,0,83,0,121,0,115,0,116,0,101,0,109,0,47,0,86,0,111,0,108,0, +117,0,109,0,101,0,115,0,47,0,68,0,97,0,116,0,97,0,47,0,85,0,115,0,101,0,114,0,115,0,47,0,115,0,117,0,100,0,97,0,114,0,97,0,47,0,112,0,114,0,111,0,106,0,101,0,99,0,116,0,115,0,47,0,115,0,105,0,110,0,101,0,109,0,97,0,99,0,104,0,105,0,110,0,101,0,47,0,109, +0,111,0,100,0,117,0,108,0,101,0,115,0,47,0,109,0,101,0,108,0,97,0,116,0,111,0,110,0,105,0,110,0,95,0,105,0,110,0,115,0,112,0,101,0,99,0,116,0,111,0,114,0,47,0,65,0,115,0,115,0,101,0,116,0,115,0,47,0,0,0,9,0,99,0,108,0,101,0,97,0,114,0,46,0,112,0,110, +0,103,112,116,98,78,117,115,116,114,0,0,0,10,0,83,0,101,0,97,0,114,0,99,0,104,0,46,0,112,0,110,0,103,0,0,0,8,0,108,0,111,0,103,0,111,0,46,0,112,0,110,0,103,112,116,98,76,117,115,116,114,0,0,0,89,0,83,0,121,0,115,0,116,0,101,0,109,0,47,0,86,0,111,0,108, +0,117,0,109,0,101,0,115,0,47,0,68,0,97,0,116,0,97,0,47,0,85,0,115,0,101,0,114,0,115,0,47,0,115,0,117,0,100,0,97,0,114,0,97,0,47,0,112,0,114,0,111,0,106,0,101,0,99,0,116,0,115,0,47,0,115,0,105,0,110,0,101,0,109,0,97,0,99,0,104,0,105,0,110,0,101,0,47,0, +109,0,111,0,100,0,117,0,108,0,101,0,115,0,47,0,109,0,101,0,108,0,97,0,116,0,111,0,110,0,105,0,110,0,95,0,105,0,110,0,115,0,112,0,101,0,99,0,116,0,111,0,114,0,47,0,65,0,115,0,115,0,101,0,116,0,115,0,47,0,0,0,8,0,108,0,111,0,103,0,111,0,46,0,112,0,110, +0,103,112,116,98,78,117,115,116,114,0,0,0,8,0,76,0,111,0,103,0,111,0,46,0,112,0,110,0,103,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,8,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,32,0,0,0,1,0,0,0,64,0,0,0,1,0,0,0,128,0,0,0,1,0,0, +1,0,0,0,0,1,0,0,2,0,0,0,0,1,0,0,4,0,0,0,0,0,0,0,0,1,0,0,16,0,0,0,0,1,0,0,32,0,0,0,0,1,0,0,64,0,0,0,0,1,0,0,128,0,0,0,0,1,0,1,0,0,0,0,0,1,0,2,0,0,0,0,0,1,0,4,0,0,0,0,0,1,0,8,0,0,0,0,0,1,0,16,0,0,0,0,0,1,0,32,0,0,0,0,0,1,0,64,0,0,0,0,0,1,0,128,0,0,0,0, +0,1,1,0,0,0,0,0,0,1,2,0,0,0,0,0,0,1,4,0,0,0,0,0,0,1,8,0,0,0,0,0,0,1,16,0,0,0,0,0,0,1,32,0,0,0,0,0,0,1,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,16,11,0,0,0,69,0,0,4,10,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,1,4,68,83,68,66,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,32,0,0,0,96,0,0,0,0,0,0,0,1,0,0,0,128,0,0,0,1,0,0,1,0,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0,2,0,0,8,0,0,0,24,0,0,0,0,0,0,0,0,1,0,0,32,0,0,0,0,1,0,0,64,0,0,0,0,1,0,0,128,0, +0,0,0,1,0,1,0,0,0,0,0,1,0,2,0,0,0,0,0,1,0,4,0,0,0,0,0,1,0,8,0,0,0,0,0,1,0,16,0,0,0,0,0,1,0,32,0,0,0,0,0,1,0,64,0,0,0,0,0,1,0,128,0,0,0,0,0,1,1,0,0,0,0,0,0,1,2,0,0,0,0,0,0,1,4,0,0,0,0,0,0,1,8,0,0,0,0,0,0,1,16,0,0,0,0,0,0,1,32,0,0,0,0,0,0,1,64,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + +const char* _DS_Store = (const char*) temp_binary_data_0; + +} + +#include "InspectorBinaryData.h" + +namespace InspectorBinaryData +{ + +const char* getNamedResource (const char* resourceNameUTF8, int& numBytes); +const char* getNamedResource (const char* resourceNameUTF8, int& numBytes) +{ + unsigned int hash = 0; + + if (resourceNameUTF8 != nullptr) + while (*resourceNameUTF8 != 0) + hash = 31 * hash + (unsigned int) *resourceNameUTF8++; + + switch (hash) + { + case 0x6abebe70: numBytes = 6148; return _DS_Store; + case 0xb444a937: numBytes = 1419; return clear_png; + case 0x7e314a7f: numBytes = 929; return dogfoodoff_png; + case 0x805edfe3: numBytes = 894; return dogfoodon_png; + case 0x4846b8f8: numBytes = 1031; return enabledoff_png; + case 0x4d153e4a: numBytes = 1017; return enabledon_png; + case 0x55e29f7f: numBytes = 301; return expandoff_png; + case 0xf2aeeae3: numBytes = 303; return expandon_png; + case 0x14ab743c: numBytes = 673; return eyedropperoff_png; + case 0xc74a0a86: numBytes = 671; return eyedropperon_png; + case 0x78ded995: numBytes = 25781; return logo_png; + case 0x48464668: numBytes = 1093; return moveoff_png; + case 0xd13642da: numBytes = 958; return moveon_png; + case 0xd5ac3312: numBytes = 941; return search_png; + case 0xca80f4b8: numBytes = 1419; return speedometeroff_png; + case 0x40c48a8a: numBytes = 1371; return speedometeron_png; + case 0x4714be24: numBytes = 531; return taboff_png; + case 0x8f1be39e: numBytes = 503; return tabon_png; + case 0xf76847cf: numBytes = 850; return timingoff_png; + case 0x10aaf893: numBytes = 867; return timingon_png; + default: break; + } + + numBytes = 0; + return nullptr; +} + +const char* namedResourceList[] = +{ + "_DS_Store", + "clear_png", + "dogfoodoff_png", + "dogfoodon_png", + "enabledoff_png", + "enabledon_png", + "expandoff_png", + "expandon_png", + "eyedropperoff_png", + "eyedropperon_png", + "logo_png", + "moveoff_png", + "moveon_png", + "search_png", + "speedometeroff_png", + "speedometeron_png", + "taboff_png", + "tabon_png", + "timingoff_png", + "timingon_png" +}; + +const char* originalFilenames[] = +{ + ".DS_Store", + "clear.png", + "dogfood-off.png", + "dogfood-on.png", + "enabled-off.png", + "enabled-on.png", + "expand-off.png", + "expand-on.png", + "eyedropper-off.png", + "eyedropper-on.png", + "logo.png", + "move-off.png", + "move-on.png", + "search.png", + "speedometer-off.png", + "speedometer-on.png", + "tab-off.png", + "tab-on.png", + "timing-off.png", + "timing-on.png" +}; + +const char* getNamedResourceOriginalFilename (const char* resourceNameUTF8); +const char* getNamedResourceOriginalFilename (const char* resourceNameUTF8) +{ + for (unsigned int i = 0; i < (sizeof (namedResourceList) / sizeof (namedResourceList[0])); ++i) + if (strcmp (namedResourceList[i], resourceNameUTF8) == 0) + return originalFilenames[i]; + + return nullptr; +} + +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData10.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData10.cpp new file mode 100644 index 00000000..588a45e8 --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData10.cpp @@ -0,0 +1,26 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== eyedropper-on.png ================== +static const unsigned char temp_binary_data_9[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,37,0,0,0,36,8,6,0,0,0,14,194,243,166,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,2,52,73,68,65,84, +120,1,205,152,65,114,218,64,16,69,127,131,146,176,100,155,114,72,193,13,200,13,200,9,146,156,0,124,2,199,11,227,202,42,98,149,42,219,85,84,78,16,113,130,216,39,48,57,65,184,129,89,224,194,75,22,222,128,197,180,123,6,11,144,1,11,121,132,196,95,104,52, +163,158,154,87,61,211,173,150,8,41,137,135,231,53,40,174,131,80,3,163,12,208,72,238,123,50,214,161,15,77,111,217,150,144,130,4,232,167,44,238,110,182,200,121,152,228,143,169,114,60,50,61,100,14,164,165,26,120,231,255,13,122,59,245,20,15,219,101,168,135, +155,237,39,224,80,111,229,110,61,197,254,215,88,246,57,170,155,6,187,213,151,88,214,204,53,221,236,12,138,111,218,197,96,145,184,114,144,128,12,64,193,175,134,7,165,207,136,171,81,34,80,60,104,87,65,254,181,68,88,17,182,34,234,233,198,106,251,76,116, +105,32,36,0,164,37,137,20,182,80,179,232,74,10,8,191,131,204,110,7,53,118,60,241,121,15,182,210,64,165,230,247,160,107,157,60,205,33,127,107,182,176,26,107,34,161,143,169,186,2,242,151,84,58,233,46,63,178,143,190,2,116,232,23,99,68,154,135,220,180,69, +239,127,244,55,25,216,31,116,126,184,158,189,245,181,104,20,61,9,255,94,2,178,130,90,1,82,220,194,196,169,36,113,198,94,5,181,14,136,74,167,174,41,61,38,206,103,115,94,210,132,218,4,52,55,40,248,71,139,237,76,1,42,10,104,165,118,90,231,49,166,62,146, +130,138,4,186,187,56,10,1,73,109,132,241,155,79,161,51,102,230,132,195,127,157,182,202,83,209,30,186,168,67,41,111,25,40,200,206,38,143,57,99,87,60,212,165,143,167,151,219,172,23,9,101,3,244,90,209,190,1,189,8,149,21,208,70,168,44,129,214,66,101,13,180, +2,181,15,64,90,225,60,181,7,64,90,115,79,241,224,76,106,109,252,127,234,122,116,208,60,204,2,72,107,225,41,149,43,47,238,169,147,21,144,214,162,200,203,135,63,28,121,248,171,44,143,53,144,139,20,129,180,104,6,240,252,39,132,46,214,158,125,16,164,4,100, +86,143,254,43,34,128,10,223,182,121,145,38,37,71,234,235,198,202,40,81,87,174,87,240,165,204,152,222,119,169,226,70,151,185,137,66,41,180,100,19,255,204,71,152,58,116,112,210,64,214,146,116,208,224,219,51,230,193,185,135,61,208,35,16,23,155,102,238,54, +173,252,0,0,0,0,73,69,78,68,174,66,96,130,0,0 }; + +const char* eyedropperon_png = (const char*) temp_binary_data_9; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData11.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData11.cpp new file mode 100644 index 00000000..f71a1471 --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData11.cpp @@ -0,0 +1,382 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== logo.png ================== +static const unsigned char temp_binary_data_10[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,118,0,0,0,108,8,6,0,0,0,191,234,70,236,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,100,74,73,68,65, +84,120,1,237,189,91,144,157,215,117,30,184,247,254,175,231,218,55,52,128,110,54,65,16,4,33,170,155,148,108,193,148,100,199,54,219,54,237,216,241,37,78,42,205,41,37,85,153,169,74,149,93,165,202,84,230,105,94,1,60,204,67,166,230,105,106,204,170,209,195, +212,164,230,65,25,64,185,141,149,40,78,16,171,17,57,178,101,11,150,45,17,109,154,132,72,64,4,187,1,52,186,79,119,159,251,127,217,123,190,111,237,255,52,154,87,145,18,237,154,139,126,242,160,207,253,242,175,189,214,250,214,183,46,91,171,191,130,195,57, +167,249,247,210,165,75,248,123,65,45,47,43,189,177,161,220,242,13,252,93,81,238,66,245,188,43,184,61,121,13,238,215,120,158,243,15,40,53,143,219,215,212,58,110,172,42,117,99,195,168,149,101,181,176,169,220,248,105,101,212,171,55,213,238,221,179,238,177, +198,45,115,162,117,218,77,63,174,204,55,95,191,105,91,115,103,117,119,71,185,34,188,99,14,58,133,123,236,180,82,221,111,20,174,245,153,179,90,221,194,245,150,114,173,238,45,221,153,9,229,115,15,58,75,248,188,91,106,22,239,113,18,239,209,63,184,133,219, +167,241,154,155,110,183,121,214,61,183,162,204,45,60,222,184,113,218,54,127,70,233,155,124,17,62,251,165,175,239,219,197,214,121,167,86,55,204,194,230,178,83,231,175,171,115,91,231,221,246,13,229,248,59,110,220,184,226,86,86,230,241,25,171,246,194,5, +229,46,93,122,248,59,121,240,190,201,117,62,198,219,90,107,167,62,194,67,171,143,240,152,8,148,7,191,240,197,139,151,212,229,203,203,213,125,107,138,194,229,181,229,27,87,32,224,53,17,244,218,26,158,187,177,206,147,160,22,22,148,62,143,199,175,95,87, +234,167,159,87,250,15,174,42,183,213,217,208,143,173,212,13,95,119,91,157,182,179,59,74,191,122,243,166,154,59,225,133,179,16,39,102,43,27,219,133,211,137,153,217,95,112,163,197,123,70,221,83,106,216,180,110,107,83,169,79,45,46,168,45,252,183,128,255, +110,238,43,87,62,184,103,78,156,240,143,243,245,7,61,163,219,205,19,184,190,37,223,114,166,189,224,238,188,161,212,146,186,35,183,251,83,133,60,175,187,115,218,159,120,124,182,58,171,212,215,126,63,115,88,107,106,121,69,169,14,132,187,245,138,114,11, +231,148,222,90,228,111,132,192,23,134,238,220,86,215,173,66,184,87,170,133,205,151,95,240,171,218,189,240,130,146,223,116,249,178,178,90,243,220,81,176,148,175,118,60,143,63,172,160,63,114,193,250,213,121,73,81,83,177,114,245,229,203,107,118,253,210, +122,176,125,99,149,171,185,210,218,117,253,28,53,17,199,43,11,215,181,130,32,183,22,107,90,221,88,174,78,20,5,10,13,198,227,212,148,123,127,122,75,67,145,84,163,125,90,43,156,244,59,119,252,73,63,246,88,100,30,148,39,108,163,191,37,191,163,214,54,250, +222,61,255,93,234,45,163,91,117,235,186,77,252,237,89,151,14,140,238,54,181,142,108,105,83,220,215,173,105,61,186,237,156,60,103,224,159,203,215,241,229,237,3,235,14,134,70,247,247,172,59,243,49,163,83,124,134,255,196,59,242,255,65,163,112,179,45,8,156, +50,62,123,86,245,230,148,187,187,115,83,171,39,149,218,253,90,230,22,102,134,16,244,121,183,240,57,165,103,102,148,197,218,173,22,47,15,104,177,242,103,104,114,92,188,168,28,46,208,92,109,41,92,17,204,15,41,88,163,62,162,99,162,173,52,43,55,110,44,195, +28,41,17,42,5,125,77,93,179,80,88,117,1,43,119,237,178,95,76,215,20,133,202,235,231,213,204,226,121,8,85,169,217,147,55,53,79,80,188,168,2,17,234,73,47,212,238,61,104,203,45,8,252,96,75,119,166,182,116,99,38,208,75,43,73,144,155,192,28,220,220,210,225, +116,20,20,97,104,76,51,10,18,92,159,93,8,76,25,4,38,223,13,204,148,10,76,122,44,14,187,35,232,197,3,165,120,95,134,251,212,142,82,83,120,142,25,71,1,159,187,183,21,5,20,112,130,219,124,191,254,200,232,218,60,238,195,34,216,192,194,57,22,220,195,123,70, +6,235,74,209,58,156,12,83,211,130,213,232,193,236,247,113,30,119,239,102,78,189,202,223,16,203,111,90,94,85,102,107,75,185,206,213,235,230,11,157,235,208,224,150,252,110,46,118,85,249,162,137,73,118,23,189,57,190,164,28,229,161,39,231,243,168,5,252,176, +199,71,162,177,254,11,104,248,18,103,248,5,105,102,86,174,224,75,95,244,194,92,199,106,165,32,169,165,244,155,203,203,199,241,3,150,213,213,127,122,221,45,158,57,175,63,77,255,5,223,69,13,216,197,201,122,236,244,105,213,217,191,163,169,29,20,226,68,123, +62,117,126,65,141,212,182,217,190,207,79,157,87,234,184,82,233,224,129,238,227,241,193,88,83,59,32,125,92,170,191,131,108,95,143,251,214,181,143,133,70,77,77,41,181,191,175,130,178,101,249,234,65,166,117,61,118,142,207,73,106,70,7,15,112,255,140,255, +61,91,253,29,55,59,59,251,150,223,56,196,251,152,188,180,222,18,204,187,225,129,18,243,77,3,222,110,148,46,124,121,100,139,167,82,19,22,75,246,165,175,43,187,120,102,67,111,190,54,116,231,207,159,135,53,218,208,106,253,190,173,140,148,82,254,138,37,246, +224,149,181,13,10,213,235,240,133,11,23,96,135,47,65,168,23,14,53,246,7,209,222,31,74,176,147,21,165,33,84,231,77,136,7,77,213,187,226,171,57,154,97,10,117,25,166,248,204,243,222,66,124,225,11,215,213,211,63,53,101,8,128,168,165,173,174,247,151,13,128, +154,62,0,77,176,114,47,24,190,122,194,251,192,182,210,253,193,182,126,180,233,53,238,216,49,248,90,8,146,143,149,193,129,169,39,83,110,48,222,215,163,177,209,243,233,180,222,190,183,231,230,79,76,235,131,131,3,21,167,218,131,164,238,195,239,60,55,15, +51,140,219,166,44,45,31,239,118,74,75,193,170,3,165,198,145,117,83,167,166,212,120,132,159,179,167,212,244,52,76,243,192,57,46,152,14,22,74,189,143,251,33,239,0,175,229,162,218,222,44,108,99,184,141,207,56,161,218,53,235,102,32,96,126,134,247,203,167, +213,237,27,10,139,104,67,62,119,225,121,128,44,250,28,28,231,206,157,151,231,173,174,226,241,75,19,199,165,8,21,141,95,248,112,91,27,151,28,132,108,39,231,244,195,10,247,7,54,197,248,137,74,87,2,188,8,77,133,48,245,149,43,254,66,77,93,94,83,250,133,23, +174,152,85,124,201,106,133,2,20,93,87,175,109,41,189,56,130,63,5,2,17,161,18,201,2,149,118,250,161,238,244,3,253,224,216,189,160,251,186,209,69,184,109,120,177,187,15,12,116,83,21,58,48,197,124,96,238,238,29,152,176,17,4,188,80,152,59,189,65,24,229,48, +191,245,153,32,130,22,206,46,204,4,101,191,111,240,176,177,131,192,68,105,91,207,28,15,3,53,12,76,147,247,245,141,137,147,150,14,235,97,48,44,195,160,49,23,135,161,13,3,252,111,28,46,131,221,65,224,180,49,110,198,152,14,254,30,195,27,149,248,155,64,179, +91,243,248,91,215,154,166,187,220,9,204,60,204,127,189,181,160,169,197,7,240,239,252,254,116,255,217,102,98,110,223,2,218,134,43,153,61,185,172,23,102,150,221,245,47,64,191,175,214,4,79,188,242,74,165,80,60,95,203,15,149,107,245,130,42,213,197,107,86, +34,136,101,1,157,154,218,235,207,247,135,51,203,63,176,198,30,249,32,1,117,151,137,242,214,228,203,42,248,81,71,1,227,11,106,174,195,9,80,250,206,206,205,128,190,136,126,72,194,21,132,19,60,1,60,206,172,212,160,165,30,180,208,71,82,59,251,13,250,60,173, +197,164,198,83,46,129,224,18,128,30,106,90,105,250,102,140,219,102,191,180,1,4,151,143,27,110,156,247,241,124,231,166,231,154,122,111,172,92,148,40,61,206,7,58,202,181,206,35,231,146,113,221,141,139,129,110,52,176,80,240,188,48,225,87,199,13,213,87,195, +131,186,109,54,149,234,245,122,112,238,252,70,77,149,197,93,151,142,154,88,152,7,42,141,91,46,131,38,215,18,231,68,139,249,20,252,179,117,199,185,217,74,139,7,0,96,253,131,19,174,209,190,167,251,0,96,52,209,12,187,102,239,21,110,247,123,240,193,56,63, +155,64,208,139,64,208,12,149,32,188,146,231,136,97,222,60,254,82,176,2,62,47,242,4,123,85,38,94,89,89,217,0,184,186,104,213,135,56,62,180,96,15,205,175,22,52,167,1,100,245,149,23,4,213,201,49,137,89,159,123,78,153,237,109,128,7,0,7,198,120,175,108,2, +33,46,42,128,35,255,153,187,93,252,160,25,165,239,192,145,54,176,210,137,104,9,94,122,149,201,157,134,246,196,208,142,33,4,171,224,30,71,219,93,61,130,128,230,230,219,154,154,137,211,175,162,164,175,21,174,140,11,60,167,65,17,53,84,6,65,134,177,183,37, +69,6,225,225,58,226,9,51,234,59,27,198,74,231,120,110,20,186,67,179,86,100,202,197,184,93,111,212,213,160,63,144,251,226,168,238,40,120,10,56,231,2,153,195,235,118,122,206,216,134,156,220,20,190,57,131,128,199,119,113,137,119,221,168,63,13,129,111,187, +102,58,43,239,203,223,48,220,203,75,106,116,88,20,54,206,22,44,99,104,62,118,114,110,195,109,220,240,166,153,224,106,115,243,186,238,116,94,179,107,107,149,86,92,89,147,239,112,5,255,17,124,210,234,241,175,63,231,31,220,28,255,64,26,123,241,162,7,73, +188,62,9,190,121,27,90,42,166,253,204,25,101,94,123,77,89,174,194,107,235,248,17,231,40,212,13,8,53,246,39,28,136,242,0,194,228,245,5,196,153,7,189,123,186,78,161,2,185,78,195,220,110,221,219,215,143,193,193,117,186,61,211,152,134,246,182,154,106,107, +179,167,98,8,118,6,90,214,3,12,109,66,237,108,107,40,159,71,193,41,200,36,140,188,64,243,98,132,191,53,53,28,42,254,81,97,169,116,212,196,154,28,227,121,41,62,63,135,192,75,173,249,183,200,134,174,86,171,249,251,240,122,128,36,91,175,67,184,33,32,170, +171,201,98,8,26,206,246,239,246,85,30,53,92,70,129,183,176,240,172,179,244,227,212,228,49,132,12,119,172,110,151,15,108,13,66,38,14,224,17,109,193,23,35,102,78,23,115,219,63,40,220,137,217,211,238,43,95,185,169,158,68,28,44,40,90,77,4,188,46,238,10,202, +32,225,194,53,2,171,27,18,65,88,196,143,250,34,60,240,135,37,49,194,15,250,196,163,54,222,127,128,123,135,64,231,231,149,166,255,32,56,250,28,66,24,134,51,128,253,162,165,119,119,150,85,11,43,191,65,180,203,147,0,13,61,129,95,125,239,187,247,148,141, +16,150,156,50,186,124,117,87,13,198,199,116,59,8,77,31,102,181,173,218,64,173,3,173,118,250,20,170,138,160,109,182,102,76,88,27,232,145,13,84,182,51,210,209,84,93,135,212,88,188,107,102,181,241,2,170,235,28,171,36,134,160,16,181,202,55,47,15,32,236,52, +85,101,79,185,188,28,155,56,172,105,4,20,54,72,160,229,3,172,128,148,100,133,179,181,90,10,31,171,92,86,88,27,22,163,176,128,214,154,77,120,221,122,160,106,61,109,163,105,191,160,7,248,76,248,105,21,196,3,23,193,92,119,226,125,247,88,112,76,117,199,22, +218,101,221,232,117,231,70,0,106,225,95,22,118,216,92,210,253,27,176,76,75,119,52,136,21,188,190,16,119,244,53,10,247,42,208,243,76,75,45,46,174,187,237,109,128,38,197,88,31,33,17,237,54,148,6,8,106,34,76,134,63,31,88,107,63,176,198,30,245,169,188,121, +148,38,19,34,2,166,100,189,18,172,176,71,138,209,156,82,95,127,73,25,210,120,124,94,134,248,175,191,15,191,3,109,237,193,151,34,76,52,181,134,71,184,131,241,158,8,212,34,54,69,0,161,172,209,38,130,112,233,7,161,160,226,9,123,0,49,97,65,115,58,210,206, +224,186,104,40,36,130,32,200,82,27,149,112,55,38,10,82,55,26,193,239,69,74,135,161,210,5,98,27,101,99,27,70,227,195,239,92,132,90,143,135,177,13,112,95,24,72,208,104,67,8,123,132,23,214,146,196,226,229,242,206,58,177,214,224,49,121,77,68,13,199,194,128, +137,159,225,117,248,236,254,126,215,166,179,77,241,191,113,218,213,198,54,237,65,9,173,79,104,238,119,213,120,175,85,18,92,77,80,244,194,34,60,58,207,193,157,165,146,136,249,6,36,185,66,235,123,131,255,47,151,159,255,252,186,222,126,113,219,173,129,153, +187,116,209,71,22,147,243,126,201,71,68,176,152,250,251,250,219,15,33,88,154,221,139,70,85,209,245,5,255,15,180,245,138,161,127,128,47,80,228,71,9,148,232,79,17,190,169,47,95,37,42,244,159,65,225,6,199,106,1,153,29,154,93,222,39,166,23,190,180,156,53, +102,31,49,38,195,149,65,49,8,34,154,91,60,113,122,86,139,191,220,131,9,166,64,5,173,26,47,64,83,102,65,81,36,46,12,199,58,128,169,45,74,111,134,3,8,172,40,253,103,38,20,134,213,15,127,99,12,205,69,200,18,52,160,201,147,191,5,44,52,23,78,0,186,177,136, +93,17,240,68,142,32,84,191,96,96,121,45,23,10,5,76,97,235,129,179,57,125,50,191,3,204,241,4,148,229,17,180,124,220,117,211,101,195,110,199,60,91,251,234,137,118,203,118,183,75,17,194,8,134,99,27,254,150,215,69,184,237,210,205,192,60,223,190,5,82,3,44, +22,77,51,209,51,181,225,220,57,175,189,244,180,56,183,150,22,17,23,156,95,98,25,109,39,8,65,235,143,68,176,71,137,125,9,164,149,143,193,214,141,248,6,34,95,56,135,22,204,239,151,191,168,220,236,51,42,216,253,253,13,167,158,173,155,54,66,25,210,127,19, +161,30,5,72,124,229,110,175,107,18,196,158,141,190,54,99,8,213,154,129,33,146,229,201,3,149,43,154,153,89,8,50,79,28,85,58,132,240,134,217,88,94,219,78,83,51,30,143,85,0,1,22,208,78,53,246,194,133,125,80,46,209,38,35,200,109,198,42,203,32,212,18,168, +54,192,107,75,127,106,2,92,47,203,177,11,129,134,248,88,12,193,231,163,24,39,127,172,176,144,68,224,42,182,30,184,56,106,241,72,69,208,238,34,79,29,0,176,213,169,181,19,240,53,176,214,38,64,230,201,172,115,249,232,192,101,16,110,154,192,255,66,147,3, +104,112,3,113,238,3,89,12,165,245,136,121,193,205,76,41,199,88,151,97,223,75,227,179,150,22,206,115,205,36,115,142,248,90,8,148,97,17,244,199,86,138,244,125,193,212,7,138,99,39,28,176,100,34,68,87,189,214,210,84,220,184,177,237,174,209,225,3,37,49,187, +241,101,248,12,181,189,33,239,59,123,42,214,20,42,217,163,33,126,12,137,216,20,66,125,4,102,88,52,53,232,154,126,217,15,90,51,51,136,47,97,197,218,3,99,135,3,67,116,75,77,237,34,92,117,117,99,50,8,51,47,106,122,208,133,174,232,177,129,106,152,176,76, +53,194,31,179,223,203,12,222,42,204,35,13,67,110,66,203,235,46,15,84,108,162,2,202,108,112,255,168,128,194,59,255,248,0,127,11,101,162,48,45,194,98,156,7,142,175,25,22,161,45,76,56,42,248,250,60,76,35,19,232,88,7,41,46,49,214,81,49,52,65,128,133,213, +192,247,112,227,186,44,52,103,198,134,207,205,0,174,50,248,91,152,91,51,13,12,97,251,3,51,202,141,110,152,41,57,7,163,251,7,154,177,239,65,134,88,250,1,23,246,9,44,186,5,213,152,242,74,69,139,182,59,23,234,36,185,41,207,223,252,221,235,154,201,144,107, +94,184,134,0,138,202,67,161,242,241,141,141,141,143,198,199,86,154,42,126,117,114,31,194,27,255,129,96,149,200,129,94,189,122,222,126,30,233,170,22,76,240,151,145,141,97,80,46,169,179,111,21,238,83,171,200,190,108,210,167,133,242,197,25,220,183,192,24, +13,199,51,122,148,117,17,147,78,33,140,1,168,106,67,51,33,204,108,111,160,33,124,131,147,28,208,79,26,156,217,162,55,214,9,52,151,166,16,10,109,38,218,233,242,220,68,73,98,134,136,131,32,65,32,139,220,208,199,210,228,66,97,185,164,225,137,35,23,88,133, +184,23,223,63,199,125,17,148,16,143,103,253,92,5,41,238,47,149,179,208,86,203,215,231,185,42,225,80,249,114,216,96,27,227,137,46,119,101,80,243,26,14,12,238,202,220,150,226,123,99,216,230,192,150,30,89,227,189,161,189,72,24,89,3,205,37,46,96,184,212, +25,192,65,52,154,34,144,89,104,45,227,222,61,152,102,147,31,179,237,154,114,253,142,114,51,43,170,202,30,41,152,99,201,12,169,137,73,62,15,164,242,229,173,47,151,207,169,11,102,149,224,31,246,241,98,37,3,90,76,104,172,253,97,5,43,97,205,141,101,1,73, +142,38,152,166,129,40,248,197,23,241,229,102,174,155,69,100,103,102,17,206,28,165,7,201,194,148,88,254,188,77,82,253,177,71,34,65,225,195,148,177,165,49,182,7,146,1,38,151,62,149,90,10,126,206,100,240,165,131,93,168,17,52,129,102,151,207,7,165,20,196, +153,247,173,54,7,242,133,70,137,0,41,63,8,211,208,4,83,144,69,97,76,16,67,242,128,208,144,96,25,32,204,129,48,75,44,4,160,40,235,240,56,5,27,64,144,37,204,176,45,51,23,69,17,99,89,203,251,35,190,63,31,131,176,185,6,8,168,98,2,39,32,55,36,7,236,8,118, +52,164,112,11,15,182,100,161,226,190,49,4,90,7,224,162,144,27,169,179,195,158,181,179,97,221,13,113,255,120,174,175,167,84,195,230,48,201,101,31,239,204,53,195,24,24,113,174,66,254,176,141,48,12,11,201,82,176,254,140,223,68,172,155,185,206,230,208,205, +116,206,91,102,194,152,17,226,35,203,222,28,59,129,203,10,132,197,251,72,239,3,83,138,116,169,43,48,3,68,102,87,4,142,211,252,138,67,23,154,12,217,28,230,160,73,145,202,63,20,234,1,76,48,67,25,102,92,106,45,173,239,13,118,93,71,247,204,254,247,240,148, +55,15,20,5,74,146,135,254,116,148,232,160,63,212,198,13,65,249,133,153,192,99,198,157,49,196,198,235,68,201,176,127,48,145,65,100,65,54,22,48,183,188,24,154,216,2,231,155,143,133,58,8,18,252,235,98,152,88,29,242,177,18,23,195,251,237,56,46,1,141,32,53, +184,79,27,225,31,124,78,18,149,153,70,64,173,35,94,10,92,183,120,220,149,0,227,120,77,152,192,52,243,2,19,61,192,105,4,217,128,247,214,124,111,147,227,243,130,48,213,113,13,238,194,212,196,93,208,68,143,203,113,16,164,38,32,195,101,241,123,162,142,214, +219,8,247,14,184,8,144,76,26,212,61,228,225,121,81,200,49,246,225,190,50,181,101,24,10,82,168,4,81,27,55,152,219,58,175,58,51,62,43,196,168,227,194,133,135,178,184,164,73,34,184,247,149,221,7,210,88,201,54,92,188,32,174,181,130,220,242,166,11,48,189, +52,25,96,80,160,173,55,133,38,108,47,221,9,152,166,62,96,142,20,230,183,86,1,37,242,176,143,226,135,221,3,107,68,162,33,168,15,2,18,13,68,190,93,147,133,121,81,215,41,78,216,96,60,54,52,183,68,185,13,248,81,103,50,131,211,142,180,92,17,148,35,165,211, +90,12,19,12,80,164,11,106,181,161,121,165,246,2,198,32,102,165,150,243,6,80,50,191,100,224,111,51,92,199,59,104,160,33,7,105,58,62,119,18,194,27,104,224,16,38,24,167,217,241,122,192,216,6,178,181,208,90,147,120,51,237,10,87,242,177,4,90,15,43,224,128, +152,28,56,37,209,222,81,15,75,161,142,243,140,208,73,67,71,235,83,137,29,131,229,74,76,90,22,89,223,129,9,117,164,43,85,179,167,8,168,130,178,86,36,176,88,5,180,119,186,210,218,225,193,150,155,121,106,193,169,55,238,168,187,197,200,182,224,115,27,234, +180,221,240,224,73,52,153,0,234,210,67,37,99,54,72,95,212,23,127,72,83,76,224,164,60,100,186,226,203,91,152,111,117,12,191,58,51,202,76,168,66,174,58,194,247,54,168,66,156,0,40,218,3,227,64,218,147,84,37,207,27,129,59,140,242,182,38,72,10,33,220,140, +23,250,213,146,224,132,230,117,108,24,186,36,89,205,140,32,208,26,156,85,97,251,1,129,15,77,108,9,180,11,115,105,156,152,92,184,24,24,84,107,240,89,153,16,20,176,237,145,207,250,80,192,16,164,49,88,147,120,222,8,82,142,241,60,120,57,8,15,66,197,109,107, +66,57,97,33,254,203,117,1,180,237,68,152,33,62,0,86,211,226,123,184,8,130,84,161,200,17,166,26,64,0,127,211,36,2,223,28,150,52,227,161,142,32,220,72,4,12,83,107,45,240,118,1,132,92,194,36,151,58,41,35,64,181,134,77,237,176,134,88,216,82,184,212,136,125, +155,15,27,101,125,224,253,246,12,204,205,168,74,242,179,170,35,190,53,6,253,200,240,231,44,194,159,235,146,246,123,237,170,178,158,135,151,240,7,73,123,229,142,148,215,232,119,243,181,31,136,121,162,80,9,187,175,200,210,129,166,118,174,35,94,61,47,241, +42,239,146,234,1,100,107,58,251,71,64,22,204,76,109,58,210,3,34,6,164,188,92,27,48,6,104,145,225,76,141,194,212,130,108,2,34,74,152,73,164,80,106,58,135,227,133,235,131,85,205,168,213,166,40,114,248,83,152,74,152,61,72,19,60,81,172,121,159,129,64,11, +58,105,250,201,65,4,203,11,65,67,112,5,245,9,140,4,220,158,46,17,141,224,215,106,75,235,192,53,67,88,132,37,230,248,39,36,104,66,126,213,6,46,15,75,89,223,76,61,18,59,51,241,22,59,83,208,38,141,178,162,4,164,130,249,14,75,64,106,32,35,103,134,142,240, +0,230,4,129,109,129,236,156,49,57,18,13,144,163,113,42,194,74,162,95,197,138,179,176,62,78,37,96,199,176,64,232,94,186,118,160,90,189,186,29,69,48,75,208,255,221,225,158,107,232,102,73,30,92,170,62,78,144,150,161,162,132,26,97,16,171,111,212,27,227,243, +238,53,68,25,243,43,200,244,32,197,117,109,101,195,66,168,147,60,253,225,185,126,183,82,26,243,126,154,122,180,42,130,101,45,116,228,36,248,73,234,51,94,101,157,15,75,88,152,122,35,10,94,122,20,160,9,105,183,131,62,78,40,50,52,204,204,156,128,79,137, +219,72,177,129,149,57,32,70,5,155,212,239,123,126,247,0,66,117,99,106,41,133,154,5,52,193,252,221,9,132,105,97,124,85,19,33,11,132,58,130,6,14,179,2,120,186,8,233,47,233,55,33,168,8,114,13,33,12,8,222,70,48,15,49,81,176,46,178,8,167,22,8,206,68,176,184, +176,4,32,150,112,201,51,27,66,200,129,171,174,71,226,87,241,58,231,159,67,223,169,29,162,212,82,209,59,71,142,225,115,132,96,137,247,71,101,148,141,248,28,35,62,154,247,97,25,65,116,133,128,56,250,252,60,202,131,17,191,59,110,195,12,27,198,221,1,129, +224,200,8,143,61,29,55,244,184,232,3,76,195,197,76,227,57,181,105,93,98,37,188,86,224,235,137,191,133,75,219,164,31,6,216,188,117,154,53,7,234,209,68,153,187,160,30,47,93,92,71,170,239,58,100,241,28,57,3,67,171,89,185,68,200,243,221,57,100,243,94,66, +245,47,210,135,69,87,60,158,243,68,181,185,182,190,174,24,175,206,62,115,51,96,112,173,170,212,27,75,87,234,77,163,31,45,30,4,140,85,9,20,58,93,99,218,88,138,205,42,174,51,179,129,228,59,115,106,18,204,47,195,152,160,18,106,32,254,209,4,3,92,10,10,208, +22,80,148,2,16,199,68,160,249,66,2,157,34,231,105,208,136,103,35,147,151,208,189,208,37,20,150,117,94,104,142,194,198,169,226,95,176,248,49,30,137,252,125,97,64,65,66,77,99,94,135,197,15,248,215,186,28,160,84,27,121,14,23,11,66,92,151,149,33,162,154, +16,90,143,215,218,80,209,170,19,128,241,221,152,109,130,62,19,96,193,83,132,89,81,72,140,140,176,22,32,13,130,207,249,94,153,1,239,40,128,42,109,48,62,207,66,107,134,166,61,135,53,4,237,29,239,246,5,103,248,147,134,60,5,104,85,17,110,21,18,102,167,183, +204,51,112,107,189,187,74,24,169,213,181,227,114,255,197,139,171,150,25,179,181,42,68,66,216,41,18,125,183,92,237,123,250,88,102,112,222,126,31,209,217,246,246,188,254,220,231,90,154,73,227,217,30,194,155,19,161,190,173,124,181,160,164,222,110,27,157, +2,1,111,119,15,76,251,137,57,51,194,137,192,247,54,182,5,86,233,206,64,7,179,248,241,52,96,61,184,44,91,5,254,21,13,72,196,235,79,12,24,67,71,13,5,34,181,222,151,242,100,66,87,2,104,179,201,112,34,169,142,56,223,32,25,200,52,145,221,163,13,135,180,120, +29,190,213,194,37,6,128,113,142,40,12,186,93,34,210,128,109,134,210,1,32,225,254,64,17,5,225,4,225,58,125,175,147,11,68,132,191,12,109,193,122,240,108,225,30,112,200,9,150,92,14,227,239,146,34,131,3,229,51,17,184,225,130,117,129,47,71,11,62,198,69,72, +101,126,89,176,83,248,178,197,120,52,178,81,173,133,213,2,3,142,112,40,236,193,122,195,7,51,214,165,207,45,1,188,104,138,131,182,181,99,248,220,221,187,165,109,60,6,86,10,190,118,188,179,80,10,102,1,51,181,240,188,114,95,4,155,247,249,121,37,57,93,146, +21,228,16,46,92,144,48,200,85,170,251,22,173,13,223,71,99,69,91,153,38,36,79,41,89,66,112,193,80,86,160,224,154,222,124,109,217,205,254,212,77,221,0,191,183,252,232,146,186,189,121,15,100,59,57,224,109,36,200,79,234,153,112,70,5,16,170,5,9,97,107,83, +38,68,150,102,212,52,18,206,0,11,50,142,9,96,170,189,79,197,234,77,88,46,100,41,108,158,252,66,66,151,178,4,115,91,250,231,128,76,128,210,224,109,32,176,146,130,198,117,249,65,129,32,7,19,64,224,20,38,252,172,217,221,190,219,252,246,183,255,237,179,251, +7,247,31,31,143,251,115,195,81,247,49,167,139,58,206,103,93,136,86,56,153,48,76,183,195,40,221,110,52,167,190,55,53,181,240,250,83,103,158,189,241,232,194,51,29,124,28,62,28,43,79,132,4,0,69,149,197,139,97,34,193,43,131,40,137,225,37,96,155,75,152,28, +124,15,205,5,50,46,225,255,13,45,52,62,124,20,217,32,205,20,209,56,62,67,92,13,161,54,105,74,219,136,129,239,0,180,118,241,244,18,84,219,76,195,116,59,29,59,91,182,84,163,0,128,58,25,24,40,129,123,99,211,150,11,139,91,88,114,11,118,22,10,48,179,165,212, +231,206,93,87,103,206,43,253,218,149,215,224,103,231,41,84,15,14,222,227,208,239,35,216,195,98,102,185,243,146,47,227,96,226,252,250,245,243,234,105,161,192,206,202,67,201,220,86,240,234,119,141,110,34,139,81,3,177,127,188,29,6,52,193,172,116,176,166, +111,142,181,90,166,3,70,9,164,191,65,42,86,76,177,33,96,162,201,197,9,136,105,129,83,158,153,60,192,249,50,20,170,66,12,72,223,70,114,33,137,37,7,96,202,17,78,47,12,40,150,35,195,33,104,41,148,3,210,38,19,209,233,188,217,250,250,55,254,249,175,236,239, +221,91,25,244,247,158,150,20,23,44,38,179,23,48,145,12,125,44,41,13,41,34,210,4,214,56,145,188,77,229,225,79,132,157,78,210,198,235,143,45,62,243,123,79,60,241,233,141,211,39,151,183,1,164,144,143,149,135,65,49,17,186,145,82,14,37,221,131,116,109,14, +66,130,198,31,40,205,21,68,207,142,232,77,135,228,47,232,28,138,40,231,2,192,201,108,197,80,65,228,14,154,32,38,192,42,55,28,144,50,131,174,97,9,254,198,186,153,86,211,246,118,173,101,109,85,23,138,207,60,238,107,111,12,75,207,9,224,232,35,138,222,94, +182,11,159,187,174,183,182,186,142,52,174,232,25,50,64,19,249,188,93,99,191,175,96,89,70,202,172,130,176,30,184,239,183,175,42,67,178,122,156,120,223,187,139,172,205,66,124,90,234,147,164,114,224,77,152,98,64,222,246,49,4,22,225,48,172,17,175,226,24, +117,199,65,30,215,97,109,131,48,131,80,21,51,53,56,195,68,191,185,34,151,132,211,166,189,48,213,176,96,22,39,4,16,198,191,37,237,93,32,255,145,231,101,1,18,212,4,143,104,250,194,189,254,118,227,143,254,232,139,191,124,247,222,171,191,130,83,139,148,184, +165,69,214,202,78,126,160,7,129,242,195,161,13,80,60,24,90,199,236,32,226,94,72,128,167,195,48,113,132,40,73,132,44,30,183,152,157,121,100,253,179,63,246,119,254,175,69,8,24,191,148,252,8,162,91,47,96,129,91,1,197,8,16,93,208,219,50,212,113,34,84,6,212, +72,235,89,93,71,44,139,116,238,136,139,1,63,10,102,197,106,160,1,172,147,50,33,21,137,172,80,11,137,252,225,1,210,126,16,238,40,175,23,116,184,225,116,27,95,97,71,189,177,215,46,89,90,51,51,85,56,198,180,164,26,55,20,132,187,240,80,184,43,87,214,220, +36,173,247,129,76,241,164,26,157,215,89,169,206,191,116,212,235,43,204,181,250,76,235,110,149,99,93,254,196,105,173,14,182,205,54,174,31,3,163,100,230,141,97,57,203,8,194,13,65,238,22,73,221,153,116,20,132,72,136,27,152,222,172,4,188,196,105,26,101,128, +165,117,104,40,238,3,58,20,243,27,67,120,176,106,34,84,10,209,32,77,224,196,198,73,216,34,194,13,24,33,67,12,160,107,204,215,254,224,127,251,205,187,219,223,253,229,60,31,183,66,0,143,162,40,248,84,178,194,202,23,196,2,47,187,234,6,165,61,41,183,199, +59,147,16,12,129,167,170,76,26,158,129,215,1,117,41,122,73,16,199,187,187,119,127,241,43,235,47,174,62,178,240,241,127,245,43,63,251,143,255,141,162,223,214,180,189,192,3,136,97,16,19,133,92,15,37,76,113,20,8,165,233,84,76,174,17,240,171,65,231,140,236, +16,22,106,26,101,170,0,201,1,131,172,194,18,12,5,44,204,24,196,117,137,53,178,219,27,72,228,52,78,2,221,71,134,43,61,14,171,13,208,57,236,207,129,123,118,166,209,7,213,8,194,162,171,72,254,40,183,240,252,117,253,197,47,118,221,234,170,151,147,164,224, +201,46,30,105,25,249,64,26,43,230,151,253,53,107,190,246,149,215,145,20,14,110,92,145,213,131,216,230,22,8,247,196,52,164,0,237,4,66,255,142,137,241,107,168,84,172,20,52,53,168,39,236,108,143,84,33,96,127,212,204,194,225,62,161,62,252,44,206,0,161,132, +132,40,12,35,32,76,96,40,248,223,50,36,250,68,40,11,108,105,99,34,96,1,80,138,156,1,206,101,89,6,123,123,183,230,191,245,173,127,253,143,199,89,15,75,25,100,14,128,214,112,144,81,176,224,154,51,45,164,62,100,32,22,246,176,154,210,249,219,242,175,4,174, +244,8,52,211,20,55,13,128,133,102,98,29,49,223,9,178,169,32,113,104,196,162,71,105,242,230,207,63,251,15,255,199,199,31,253,228,93,2,45,44,159,210,208,209,19,184,147,128,12,168,183,248,139,24,45,160,134,210,52,243,43,224,155,96,85,2,84,33,176,182,96, +66,163,184,160,41,102,34,223,102,240,199,176,28,131,238,129,77,166,27,110,23,9,131,227,113,203,49,73,63,234,20,194,41,47,204,20,150,237,43,147,124,237,178,144,20,203,229,145,190,38,91,153,226,195,152,118,162,185,239,45,88,255,84,22,170,105,38,124,55, +0,156,22,54,129,134,59,64,195,39,61,217,223,153,57,45,21,242,221,170,94,73,124,43,184,96,16,246,154,182,142,102,152,236,210,56,241,204,146,233,103,1,57,86,166,195,198,204,196,208,159,70,65,196,112,130,28,111,78,191,138,144,3,156,171,161,151,226,185,99, +172,90,86,102,24,182,195,188,246,202,213,191,177,181,189,241,95,129,38,106,247,7,227,120,52,28,135,57,208,216,104,132,16,67,59,111,120,241,134,57,104,194,48,102,194,198,103,221,45,97,51,31,166,242,242,186,38,226,18,246,153,188,162,88,111,126,10,62,27, +26,134,32,8,108,9,139,165,21,65,188,226,2,8,122,159,248,216,207,254,206,179,207,252,230,183,232,123,41,200,80,252,182,1,76,200,129,124,1,23,50,66,6,10,28,92,154,13,11,154,104,228,105,139,36,130,80,71,32,176,108,12,110,140,164,20,126,29,100,27,210,205, +34,97,96,32,76,230,113,251,123,64,20,101,97,219,166,41,130,125,35,204,203,195,74,71,54,142,205,129,75,88,95,183,207,65,101,87,241,147,174,248,186,40,167,142,100,222,38,130,125,111,34,217,85,188,240,26,235,229,30,30,172,110,103,244,44,109,23,96,250,239, +129,50,97,142,149,69,104,123,172,178,6,219,205,90,94,198,107,242,1,51,158,28,103,233,10,117,128,97,141,68,38,121,2,34,2,81,11,180,49,38,88,34,77,8,205,163,80,199,121,25,80,152,244,173,140,55,137,140,33,153,240,181,155,191,247,235,189,254,173,255,6,43, +126,106,56,176,233,96,159,49,102,162,218,211,45,187,240,200,124,57,55,59,93,78,181,235,174,22,215,108,45,173,3,140,38,46,78,88,141,70,119,46,69,32,92,171,226,120,121,3,254,82,51,122,241,121,73,81,96,205,5,0,11,173,227,56,68,122,47,112,44,5,32,127,12, +61,108,253,249,95,92,251,239,191,242,7,191,243,27,92,96,4,109,4,111,184,31,118,53,50,19,28,32,191,3,226,19,84,111,201,130,196,134,104,159,191,57,40,199,12,183,252,57,103,14,18,196,69,4,139,198,90,46,86,94,50,222,103,241,122,194,114,161,99,74,157,65,248, +216,152,242,69,127,52,197,236,105,250,181,207,173,122,101,92,246,150,116,82,46,163,222,166,164,239,159,221,185,200,196,238,188,254,252,202,154,158,244,158,44,62,51,23,180,62,19,34,126,69,216,129,148,92,125,176,45,45,22,124,172,165,67,169,87,98,189,47, +107,119,169,173,118,8,239,2,161,130,248,54,172,51,138,107,116,181,185,97,238,20,41,81,69,246,6,212,29,127,179,142,154,49,131,71,132,46,48,185,16,174,19,28,75,45,86,193,221,205,175,253,45,103,119,127,163,44,84,186,187,61,74,29,18,64,237,214,180,58,177, +56,83,198,224,123,184,70,166,103,155,118,110,118,202,182,167,155,174,13,1,207,76,183,92,179,134,84,66,2,164,103,130,202,213,78,132,251,16,108,148,185,152,109,234,161,166,227,68,240,234,53,220,151,76,27,37,21,86,242,114,179,185,249,221,191,255,31,255, +240,127,249,219,146,41,96,2,41,194,58,0,93,140,120,25,171,7,108,84,170,131,28,56,107,204,28,48,112,52,81,61,204,134,97,53,7,18,254,146,83,182,121,70,248,161,104,201,136,202,210,150,87,130,3,40,69,156,78,75,91,202,131,7,158,106,36,27,213,6,205,200,132, +60,89,62,62,143,5,13,18,130,74,242,253,210,164,170,197,29,45,149,9,222,46,203,183,132,58,95,85,238,197,23,255,68,255,201,246,134,234,92,157,97,139,160,106,225,195,247,193,207,238,171,105,245,232,241,190,249,203,93,96,134,123,92,109,129,217,239,130,227, +5,36,118,253,66,78,5,191,52,180,15,140,74,28,40,248,64,96,127,104,97,68,47,22,130,28,12,146,132,212,32,243,36,240,164,112,199,36,114,113,62,65,201,1,70,146,58,208,80,22,172,253,222,254,198,143,57,123,239,31,12,135,89,210,235,102,112,121,169,158,153,107, +23,9,8,121,208,131,38,31,41,129,188,229,216,155,88,48,27,46,137,35,193,80,176,108,134,101,82,206,43,38,5,38,107,219,72,176,42,82,86,226,118,241,187,217,185,65,152,92,240,53,198,139,21,112,151,201,3,81,106,222,71,91,119,208,223,91,30,149,157,205,165,147, +207,188,9,20,132,85,160,229,172,145,209,14,180,247,235,172,250,209,17,34,97,50,30,17,205,191,113,245,132,165,55,5,18,93,252,192,16,47,29,226,249,129,219,239,43,177,12,6,216,106,54,72,84,119,138,203,16,223,125,100,221,49,144,213,49,56,246,242,22,48,244, +169,155,250,254,27,179,238,212,19,74,253,234,239,40,33,241,175,73,49,254,42,219,69,32,179,135,139,213,188,93,168,218,139,93,170,209,39,101,165,107,248,239,252,121,95,117,72,255,202,21,164,226,45,67,51,252,113,112,194,211,31,15,12,147,231,172,177,101, +91,5,24,40,121,29,139,179,89,26,202,10,8,95,108,150,168,152,137,113,152,186,148,1,15,178,236,36,117,185,210,121,17,60,66,80,5,105,146,65,162,105,179,197,246,124,20,221,251,175,239,221,61,168,111,126,175,151,158,253,216,35,217,163,167,231,203,108,88,234, +254,1,194,30,151,66,91,27,64,90,44,24,174,225,130,219,186,134,215,25,93,79,107,208,190,80,23,35,216,69,87,7,32,77,113,194,35,33,88,165,72,148,138,202,159,107,36,4,146,32,183,204,201,225,27,209,90,92,55,204,10,136,58,224,207,161,111,6,192,125,245,230, +183,254,209,214,246,75,39,13,157,105,73,109,181,18,158,145,96,225,66,200,203,2,161,186,146,223,103,202,88,180,150,231,132,22,139,121,92,90,175,176,168,249,2,118,152,227,41,164,47,233,194,152,183,149,222,33,229,91,65,15,42,107,184,123,194,71,33,236,27, +38,165,11,236,35,197,132,12,71,39,0,234,45,37,194,239,166,177,135,69,224,234,97,102,135,169,58,117,67,153,27,200,2,252,228,47,181,3,164,14,213,34,76,241,108,68,106,158,245,75,212,80,200,12,24,145,73,102,38,155,51,73,150,143,193,84,212,131,186,245,192, +105,196,196,185,36,200,125,98,156,81,32,145,47,133,74,190,54,18,191,106,133,200,39,175,59,216,251,234,255,112,176,55,56,113,255,222,126,180,252,201,165,108,103,251,192,236,220,31,134,187,247,179,48,10,19,49,212,80,25,18,13,18,173,36,53,132,25,217,88, +147,250,65,110,208,110,223,237,33,246,181,8,82,11,5,116,163,114,216,124,92,228,58,162,207,106,101,139,129,150,216,151,128,10,203,5,52,101,32,4,6,208,177,246,224,74,116,150,214,128,207,146,232,41,173,213,94,254,220,175,94,186,8,16,78,54,25,102,202,21, +134,245,25,8,206,36,190,5,58,198,10,46,220,33,50,14,11,198,185,112,39,14,137,163,162,222,142,11,60,199,146,98,99,149,198,208,166,69,139,174,97,96,75,41,97,237,131,102,204,240,229,71,5,226,87,8,252,141,126,121,242,51,190,186,226,220,98,215,93,171,42,43, +124,203,170,122,75,129,155,121,167,80,47,189,69,216,203,213,56,129,133,77,100,23,86,54,212,202,179,190,187,124,106,38,144,102,36,246,215,196,85,85,0,83,150,242,119,70,251,218,95,228,87,235,16,106,129,147,201,21,42,37,44,149,198,186,88,121,176,164,229, +183,106,57,107,101,41,128,132,148,17,18,158,230,214,95,254,254,111,220,221,60,88,124,117,99,183,246,227,159,61,91,236,237,140,204,235,127,177,159,12,15,130,160,81,111,187,52,174,187,56,170,185,56,76,85,13,218,153,68,41,78,109,164,90,205,186,45,70,44, +252,77,84,45,105,2,76,53,96,154,83,121,30,159,159,224,111,20,112,81,16,214,42,31,15,49,202,245,166,218,147,27,78,248,12,81,102,37,228,162,168,196,4,122,201,237,209,96,248,212,127,248,198,255,250,107,178,20,104,109,12,1,31,100,5,232,199,124,177,170,126, +163,99,170,177,210,218,73,73,79,187,70,242,109,44,244,42,93,22,107,149,157,30,120,173,102,143,82,86,121,204,121,47,7,118,242,145,151,103,55,197,117,223,180,39,197,110,180,172,151,215,212,59,142,119,128,167,11,85,229,249,132,170,98,204,196,246,71,38,124, +151,241,223,206,189,194,189,254,242,200,182,59,165,155,63,238,95,195,38,37,86,68,140,147,190,238,169,190,128,38,17,112,152,186,62,50,208,210,78,65,48,65,62,94,69,42,168,42,29,20,139,113,181,100,86,128,150,11,225,132,105,237,184,136,107,181,193,252,189, +55,223,248,123,189,78,25,253,216,179,103,178,122,45,202,95,121,233,65,154,166,109,8,171,5,161,54,92,28,167,42,141,18,149,196,53,17,84,154,214,112,61,85,173,169,134,158,153,169,195,105,38,58,9,235,138,207,75,162,26,158,139,11,133,26,114,49,224,18,48,211, +23,136,221,242,45,190,94,102,150,1,204,184,196,5,57,86,100,232,73,114,105,161,62,172,152,59,231,211,101,242,207,214,230,237,223,220,239,239,212,75,237,147,23,1,136,18,104,38,222,22,22,8,128,170,148,140,21,113,152,175,199,114,88,216,72,221,225,121,74, +39,186,102,232,170,104,142,97,161,205,52,67,195,172,175,153,226,100,11,231,48,241,9,120,181,192,127,150,20,187,18,25,207,62,255,235,53,253,10,66,79,154,100,181,186,206,102,184,42,98,127,15,193,78,212,216,55,47,95,49,44,173,89,101,161,213,150,114,175, +109,109,232,141,27,27,42,124,42,53,143,227,194,58,225,187,59,190,46,184,61,11,194,97,212,116,172,97,2,50,213,113,196,166,165,32,96,153,11,67,28,254,136,162,204,145,28,97,225,25,219,16,125,197,3,137,5,15,76,60,209,95,208,125,177,78,2,144,226,246,171,127, +242,119,242,60,8,142,205,79,231,143,60,214,202,58,15,250,208,210,38,130,123,209,62,8,42,134,16,19,23,197,49,180,48,118,73,2,109,12,112,155,45,118,96,114,155,211,137,154,155,175,217,180,150,138,134,38,97,13,207,75,229,66,225,198,213,37,130,112,137,211, +252,89,209,162,146,226,169,72,47,64,192,217,144,86,150,33,15,121,103,198,61,124,138,173,252,25,156,64,81,52,191,241,231,255,226,151,137,125,248,80,33,225,15,75,117,124,73,14,92,48,203,169,140,145,144,7,56,66,22,121,166,199,200,74,209,146,5,209,72,51, +236,81,190,31,76,177,55,168,133,176,103,111,111,79,210,121,84,158,6,194,30,54,175,181,230,110,9,135,112,21,166,152,207,189,241,121,112,198,171,171,21,121,234,222,59,220,153,152,98,254,119,101,101,195,185,35,109,28,180,235,203,43,203,10,185,116,197,22, +13,146,18,76,51,177,147,124,52,238,106,182,56,116,118,16,100,35,139,110,165,205,130,25,148,135,213,249,205,36,33,89,65,100,172,51,152,38,214,42,49,43,45,168,147,46,202,9,234,20,134,127,120,112,103,254,206,27,111,254,92,61,105,9,164,220,219,46,194,238, +94,16,199,17,180,15,130,11,145,38,71,22,92,177,194,144,194,149,11,66,154,246,108,221,77,77,215,221,184,31,235,1,76,86,90,79,117,45,197,243,224,139,43,173,21,33,83,184,73,101,150,39,194,213,18,207,104,85,113,85,202,171,49,110,148,108,185,20,222,64,184, +74,143,160,181,243,81,177,228,172,213,189,237,91,191,218,61,216,110,152,138,0,7,237,41,218,27,133,82,48,128,144,167,144,58,44,89,196,56,55,44,94,119,38,151,186,174,110,31,218,212,28,27,214,39,119,246,129,1,59,164,99,219,210,101,207,158,250,121,118,239, +35,236,97,60,203,1,39,212,216,167,159,158,50,91,175,116,157,39,24,160,177,151,42,197,84,239,129,138,249,37,197,20,95,196,7,95,184,104,5,61,85,141,185,231,241,95,255,214,45,179,84,61,151,132,63,43,15,101,52,0,160,156,157,214,166,201,6,211,62,208,28,159, +48,2,2,101,11,70,233,125,43,87,41,205,48,127,92,2,147,132,164,151,41,193,23,3,164,178,214,95,147,67,117,196,50,184,255,205,205,155,203,101,22,232,52,169,43,106,100,191,171,76,214,15,224,71,155,16,102,44,2,149,75,136,60,59,194,154,36,141,177,186,99,55, +125,172,230,166,231,234,106,238,120,221,62,250,100,211,214,155,137,42,88,222,74,115,13,141,78,211,196,165,181,186,107,182,82,46,6,106,184,162,73,166,25,15,77,84,217,87,175,185,206,51,84,194,82,81,136,142,230,57,247,2,182,132,211,74,136,104,81,6,91,150, +205,63,222,248,151,63,39,61,83,36,57,112,97,93,21,11,234,4,86,209,28,177,167,183,148,200,7,240,138,150,26,72,49,136,29,124,139,156,79,214,38,79,77,107,93,71,168,211,237,178,73,116,74,1,98,154,109,100,214,103,23,246,164,54,155,252,60,39,232,236,238,102, +110,1,57,113,182,212,48,25,48,225,40,220,17,173,125,155,198,178,217,234,82,21,234,92,14,38,43,97,117,89,185,235,32,255,79,128,179,228,244,20,210,92,195,158,115,59,148,95,99,95,43,82,38,248,50,181,214,192,212,154,164,17,71,18,230,72,85,4,132,202,21,106, +139,216,23,143,85,224,193,216,73,92,89,146,32,150,21,77,230,135,165,41,163,209,131,95,78,144,60,160,22,146,93,26,116,93,80,140,2,147,38,53,88,129,68,5,20,110,2,95,29,83,184,145,154,57,94,3,51,20,233,222,3,96,148,81,8,173,76,116,156,32,79,218,151,58,23, +48,91,137,138,12,120,76,152,8,3,51,205,74,222,40,140,189,57,22,225,38,162,213,1,83,188,71,132,235,249,9,156,148,210,249,230,37,185,120,129,87,26,59,161,179,212,206,131,7,63,78,22,170,196,194,20,237,36,86,168,64,20,236,11,4,12,13,142,99,35,133,118,172, +185,66,122,135,36,69,129,28,47,207,149,99,249,94,1,78,117,223,218,150,239,188,22,115,156,182,142,235,81,207,247,221,50,204,252,211,111,20,110,19,214,147,25,30,142,127,224,253,203,203,239,140,110,222,6,158,156,158,104,236,11,236,15,169,94,176,190,225, +107,135,123,119,207,186,25,240,150,28,250,193,178,210,57,246,129,182,218,150,69,88,173,86,83,21,187,206,13,6,156,249,0,64,128,44,206,254,104,36,93,109,242,206,73,46,177,170,148,117,178,70,23,247,129,182,208,88,194,92,223,2,154,24,20,180,230,242,57,248, +182,83,16,162,196,156,1,46,229,24,17,104,163,166,164,95,131,2,137,34,199,251,113,162,92,72,141,222,131,233,15,89,118,29,234,225,65,40,21,141,227,94,72,65,35,75,215,80,172,144,73,146,216,53,218,52,15,184,167,65,166,42,21,237,247,66,77,189,214,86,96,202, +77,132,91,133,244,50,132,137,158,204,78,132,58,249,123,216,241,165,135,163,253,143,247,70,247,235,134,153,122,205,192,143,105,28,68,108,248,206,100,161,138,28,146,181,44,100,151,62,3,113,79,109,164,57,24,86,179,135,87,122,116,67,208,55,115,124,203,158, +60,167,30,79,73,191,15,51,103,244,179,111,17,213,58,135,158,173,203,125,107,27,239,204,238,188,35,109,231,147,235,23,124,58,72,249,140,14,80,177,212,15,247,23,111,153,198,126,168,31,28,35,24,64,160,5,170,101,42,240,100,4,209,28,73,129,38,116,112,88,171, +5,101,78,218,105,172,16,197,1,114,0,92,177,92,20,26,134,228,154,152,96,23,136,127,210,228,76,73,72,136,119,8,2,245,198,235,223,254,248,120,100,131,70,19,102,28,160,8,204,20,171,201,240,56,52,17,145,22,146,219,138,149,104,228,172,140,76,53,193,186,8,171, +132,141,48,69,44,108,41,192,240,176,78,188,201,183,4,14,178,10,209,23,168,249,146,39,208,5,137,81,227,136,180,161,37,173,128,12,156,149,215,49,22,70,184,133,24,183,194,35,101,197,87,89,165,39,218,170,143,170,169,92,39,187,100,200,62,5,223,121,245,63, +125,242,167,126,236,115,255,5,148,18,22,97,137,4,21,8,14,196,179,33,171,180,72,88,210,14,247,50,154,45,158,51,90,117,184,37,70,68,44,92,29,170,70,138,69,152,240,59,53,85,3,161,18,178,241,174,86,226,87,246,173,106,227,75,245,17,235,178,105,90,61,57,101, +118,23,207,151,204,205,46,47,3,64,249,249,37,90,29,73,6,188,69,176,85,30,86,174,87,149,231,186,243,219,215,205,23,190,160,100,202,11,215,26,11,154,85,224,219,54,134,96,69,160,117,138,157,221,96,156,92,99,10,104,152,172,234,16,223,58,81,174,86,19,44,161, +217,47,17,32,40,183,161,52,78,80,48,32,10,88,214,226,125,17,253,18,184,87,240,102,161,122,243,206,157,199,97,110,157,7,72,36,226,33,92,152,207,146,109,148,165,118,237,217,72,103,227,194,165,117,224,146,32,16,20,83,140,148,196,36,12,64,33,31,196,151,136, +160,187,49,76,112,192,14,59,144,19,185,200,34,18,153,140,53,80,39,34,17,228,73,243,144,221,115,82,240,196,116,13,83,125,178,48,88,245,20,228,62,244,177,206,251,94,143,155,164,177,67,196,130,251,3,201,201,121,161,243,119,118,246,182,207,224,177,175,147, +229,40,133,19,101,161,58,222,164,144,222,3,240,33,153,102,242,22,107,146,76,6,235,178,152,136,112,134,197,210,80,219,1,91,128,34,184,160,168,167,187,7,165,203,145,216,229,52,26,182,164,28,212,141,62,214,92,96,43,136,122,233,37,101,23,23,89,162,116,222, +205,92,61,207,233,6,44,130,120,75,109,241,59,80,177,215,218,75,50,241,133,83,77,206,45,158,119,164,19,137,198,186,59,133,99,149,58,159,211,31,60,52,13,44,129,153,61,198,36,65,195,223,1,211,162,70,67,197,52,57,123,106,152,117,229,28,16,11,78,55,23,66, +66,11,57,94,48,190,49,81,149,45,149,10,48,0,29,119,74,252,29,174,204,45,212,32,72,154,91,22,114,35,211,130,245,84,107,6,69,179,29,178,12,14,148,31,27,105,2,51,127,42,118,179,39,105,224,51,61,56,0,215,155,133,4,38,138,160,105,118,17,241,109,45,4,192,10, +0,160,2,181,112,166,134,247,8,29,23,13,45,6,179,56,4,78,124,190,44,38,216,198,144,102,221,25,117,36,177,41,25,60,79,76,120,255,234,179,67,2,162,252,121,227,79,29,13,78,150,236,33,42,75,1,137,200,199,29,158,163,210,250,8,32,106,120,63,155,1,120,32,241, +164,194,10,72,49,44,100,191,210,56,30,232,239,221,194,155,1,183,176,253,146,211,114,228,13,128,140,57,251,138,89,30,8,85,222,247,198,164,75,96,109,242,85,223,3,21,123,173,245,131,45,47,175,172,81,197,15,159,40,57,88,8,117,110,33,241,175,121,163,176,132, +227,172,104,239,218,166,84,185,179,101,131,5,224,28,253,192,100,122,80,53,35,83,184,65,213,147,106,89,130,173,164,50,80,180,85,14,86,230,11,3,167,200,155,206,33,46,133,9,13,16,190,36,170,62,29,148,25,212,27,233,60,71,114,63,27,146,232,8,245,226,19,13, +53,115,34,176,243,167,2,11,18,74,53,129,47,74,124,16,98,99,18,155,120,126,160,154,115,129,226,99,181,25,171,206,254,68,205,158,60,27,168,99,139,137,157,154,169,185,197,165,182,123,252,227,83,182,86,199,243,102,35,55,59,31,227,92,38,206,215,221,80,192, +212,54,82,139,204,211,146,78,174,24,99,249,127,34,92,207,47,79,132,61,202,186,143,48,173,66,239,64,43,196,46,3,2,66,45,0,202,183,156,228,131,156,10,173,227,201,137,77,88,75,156,58,153,141,177,7,212,12,20,223,0,50,166,21,108,137,155,235,40,142,3,228,252, +71,98,27,22,183,177,251,125,115,83,74,150,36,31,203,212,29,93,232,123,162,98,191,52,47,56,239,98,47,41,95,114,177,174,152,92,231,99,212,216,209,203,165,227,172,65,245,168,175,129,29,212,247,61,253,213,155,116,160,43,77,133,29,195,240,136,153,225,249, +9,16,192,6,62,133,69,219,159,87,210,100,137,183,124,9,9,0,165,220,144,149,254,199,8,140,232,48,251,7,227,242,254,157,94,128,64,93,250,238,12,88,32,112,184,234,19,127,3,2,105,89,247,200,199,141,59,251,201,134,107,207,107,215,235,142,204,233,149,41,16, +233,124,63,36,148,96,166,211,26,96,83,29,214,47,33,56,137,245,210,227,109,154,111,34,106,157,212,99,222,7,171,80,119,115,243,117,85,111,198,204,132,51,243,64,144,6,249,196,242,62,110,82,159,96,36,177,231,147,0,19,111,230,212,67,127,235,152,244,40,155, +186,90,176,124,169,173,122,207,185,126,243,220,255,112,134,105,116,10,236,30,19,107,198,161,96,192,25,67,92,24,203,210,232,177,160,158,25,29,230,183,27,181,105,153,245,40,167,12,177,230,110,211,43,27,173,40,251,122,94,120,65,233,229,27,147,9,170,15,229, +248,174,249,88,152,98,153,47,196,159,180,189,188,42,67,31,105,138,39,147,78,152,113,104,116,173,219,229,140,133,193,148,227,120,156,158,32,185,62,204,110,221,17,17,79,180,149,181,63,94,83,51,164,166,144,0,201,133,83,22,255,169,30,42,44,105,24,191,128, +107,129,88,101,74,121,231,222,136,37,129,146,245,123,228,137,122,142,12,129,155,93,8,203,126,47,215,199,22,227,178,217,138,44,120,15,27,227,130,215,169,151,255,248,0,234,28,59,10,143,117,223,221,29,109,238,222,116,250,96,59,212,119,94,25,201,226,169, +213,16,10,225,228,82,25,162,88,98,87,23,129,240,143,229,132,107,34,110,152,108,66,85,162,174,144,238,17,113,170,32,51,45,9,7,43,89,158,67,141,117,149,80,189,82,20,117,130,53,109,124,232,102,205,195,83,205,94,34,72,21,8,223,87,119,28,61,24,202,82,25,162, +41,174,102,78,172,113,46,157,107,186,100,60,115,248,122,54,142,207,116,150,156,140,126,88,145,70,56,25,161,75,154,120,173,74,0,28,133,198,239,74,41,78,134,70,241,185,107,243,15,99,36,63,0,18,241,212,19,150,131,25,204,172,154,85,236,214,102,225,90,84, +13,195,10,145,217,225,124,165,209,145,247,45,203,201,212,132,92,128,147,160,152,188,138,97,131,42,168,136,189,239,30,33,49,111,0,251,29,75,113,65,210,209,24,101,89,169,239,223,29,6,237,99,16,214,94,25,236,222,203,21,81,51,178,35,186,179,219,87,175,188, +180,21,222,126,185,167,147,168,9,127,90,135,127,14,152,133,129,239,15,133,101,10,97,67,26,237,84,202,171,34,220,23,225,179,106,117,156,77,184,192,246,84,147,183,37,17,191,176,52,227,230,23,154,229,244,76,138,133,130,60,58,203,29,180,169,160,177,0,75, +145,168,155,228,103,37,18,154,196,180,62,81,32,62,150,235,180,168,132,137,43,225,164,179,143,185,91,246,231,178,201,90,249,39,54,26,90,202,68,34,70,89,219,206,50,25,32,231,18,166,120,31,255,221,124,213,191,95,77,38,191,221,145,9,171,52,197,136,93,205, +196,199,170,67,36,240,240,156,191,35,209,206,117,185,186,58,175,201,120,92,187,118,77,111,255,187,109,245,250,104,209,144,241,200,134,112,165,8,34,70,169,209,163,253,61,4,220,13,61,197,42,48,216,172,154,141,117,111,48,16,128,80,166,177,113,214,39,205, +35,137,199,67,99,146,144,165,61,100,13,217,202,17,148,108,192,192,205,82,58,20,137,255,217,120,238,76,183,255,234,47,1,252,164,160,14,217,138,133,133,224,157,20,180,83,159,250,88,189,4,214,50,221,221,92,109,222,238,5,125,56,117,104,119,208,185,175,3, +178,82,218,214,171,220,188,132,66,234,204,167,34,221,154,181,106,234,132,114,141,105,118,27,32,220,129,96,109,21,143,102,227,28,231,189,168,234,160,232,231,121,88,233,26,40,193,204,35,177,175,157,7,71,194,15,43,159,147,215,147,248,86,251,163,170,150, +19,95,92,126,226,233,191,249,47,4,81,133,154,145,19,139,122,28,139,98,164,63,179,204,74,233,3,40,216,208,27,75,243,116,6,139,166,19,248,44,214,25,196,140,157,234,174,94,203,212,48,100,135,96,211,114,234,91,35,244,165,148,73,177,104,57,83,227,187,200, +157,188,246,154,82,79,32,225,190,125,28,249,217,107,224,244,175,225,11,172,122,107,203,227,45,225,206,164,3,128,143,113,136,52,252,173,90,191,180,46,115,131,239,86,136,24,198,214,169,77,100,168,242,105,126,88,176,135,127,90,52,197,123,90,145,45,42,218, +35,157,237,14,85,152,134,240,189,126,44,64,238,38,154,235,107,205,242,137,17,8,25,210,73,134,0,9,106,235,184,178,91,83,105,127,167,151,181,171,158,11,118,152,3,209,34,127,80,15,245,51,159,157,26,111,111,13,202,63,61,232,167,79,62,61,95,6,113,97,242,113, +238,158,253,185,217,242,205,155,125,125,247,86,102,70,29,154,98,200,22,68,87,119,119,172,206,124,178,129,240,39,151,115,141,48,73,223,189,179,171,154,0,73,56,181,170,57,5,237,30,128,131,31,56,209,90,118,46,68,65,136,68,2,8,17,248,151,40,14,52,210,200, +0,101,197,36,162,209,82,140,33,5,9,94,89,141,136,222,223,8,77,178,35,109,36,144,145,3,69,72,20,2,250,137,153,2,44,38,152,251,180,14,112,201,147,194,26,174,42,60,177,148,179,19,127,204,120,50,129,187,233,31,176,192,173,180,201,188,214,123,247,145,247, +74,253,60,229,206,204,29,176,84,75,238,233,68,153,221,57,73,178,203,123,32,23,251,54,125,125,23,83,44,97,142,162,96,159,51,87,94,184,162,57,228,146,35,226,38,225,78,7,4,5,7,66,178,155,142,163,94,39,125,158,25,190,80,218,26,26,65,118,96,114,66,124,89, +178,78,89,101,154,16,253,176,103,70,250,86,39,203,137,53,246,208,24,241,177,244,139,236,50,174,183,234,111,228,50,212,193,10,171,71,147,44,28,45,242,211,240,173,44,70,117,91,111,60,80,223,253,78,47,120,233,107,35,16,227,129,233,220,199,119,219,207,224, +119,67,213,152,179,174,49,171,221,153,31,139,221,147,159,106,217,209,176,112,48,171,248,252,12,90,222,145,159,255,96,19,208,210,147,70,62,3,135,108,18,179,56,240,12,46,7,189,41,194,5,83,53,51,211,176,52,233,82,230,98,124,100,35,101,53,76,227,73,238,221, +30,161,27,57,170,47,221,57,106,6,199,165,132,122,126,100,2,221,143,160,39,254,86,111,138,7,213,52,56,158,39,230,42,83,210,234,249,64,58,242,212,219,14,118,90,50,249,194,112,231,165,177,8,212,62,143,116,42,139,249,39,157,119,71,159,255,46,26,171,75,159, +2,146,222,16,25,36,66,240,196,97,209,119,119,110,42,86,166,63,232,69,138,92,113,13,39,133,29,117,17,242,135,179,69,0,31,103,56,187,208,56,176,60,174,145,88,152,51,207,160,225,103,4,120,114,2,244,99,11,33,216,217,141,200,66,148,128,21,221,200,87,2,103, +64,206,56,35,231,158,90,122,243,230,75,91,42,183,25,249,93,81,20,118,74,117,182,199,193,181,127,179,149,196,205,18,198,182,166,178,33,146,34,211,145,25,247,67,181,187,73,64,194,100,123,224,78,156,118,234,141,191,28,155,151,254,75,174,23,206,25,55,61, +47,240,84,189,252,103,119,129,130,19,38,11,244,212,92,228,198,163,28,160,37,86,52,187,89,158,187,44,183,38,69,38,136,51,50,145,88,208,160,110,21,153,47,196,185,136,163,10,144,34,101,101,114,125,233,189,39,188,39,145,142,15,52,106,105,251,22,201,175,18, +217,72,150,36,203,130,207,10,23,224,23,150,65,14,95,227,115,191,165,120,130,200,66,190,50,36,133,52,7,5,76,255,181,135,180,221,220,12,206,87,115,74,239,14,253,140,40,86,129,222,124,61,47,31,93,90,82,7,157,135,74,9,0,197,199,57,254,240,176,57,235,93,53, +182,18,175,56,140,138,160,48,236,139,229,24,116,230,98,121,144,82,100,171,228,96,228,87,21,187,196,178,251,48,175,99,166,236,252,12,194,160,173,100,228,29,181,86,26,89,232,64,97,205,160,53,48,195,57,137,97,48,75,185,180,181,197,236,44,15,73,43,150,210, +113,254,232,169,199,239,252,210,223,251,88,191,0,155,193,162,47,209,14,229,233,90,24,55,53,216,133,223,108,180,104,110,69,231,89,55,204,176,164,213,70,178,29,73,131,122,43,64,172,77,183,31,186,205,191,208,250,245,63,203,216,3,105,63,189,122,198,206,28, +111,112,29,185,189,7,125,197,194,52,190,7,66,42,55,236,102,122,170,221,112,68,201,163,126,41,35,15,96,142,229,241,36,9,29,27,167,39,220,49,227,111,17,163,164,114,220,195,226,122,220,62,54,123,242,214,91,78,46,62,44,145,110,78,45,137,0,158,7,186,163,32, +228,176,139,76,55,176,112,105,134,249,220,225,112,40,67,61,79,144,255,199,226,202,239,251,153,201,204,201,238,130,190,101,197,138,170,246,41,224,177,176,64,83,124,69,46,149,80,223,210,252,252,142,180,221,209,131,233,190,11,184,252,218,231,148,204,220, +61,57,119,214,221,125,121,100,57,58,110,62,247,171,41,169,202,98,216,169,206,9,101,204,113,66,216,50,205,140,211,84,56,4,139,168,152,4,5,81,126,0,130,130,166,215,114,30,4,87,117,232,79,128,230,42,7,18,250,206,55,212,159,127,230,103,207,14,142,47,197, +101,175,63,228,180,88,35,221,141,192,21,123,59,185,25,13,56,77,205,232,241,168,52,121,86,170,28,177,103,218,2,163,217,45,220,160,99,221,253,91,142,8,216,205,46,202,76,9,149,247,83,245,103,95,237,234,91,27,251,48,149,218,62,118,102,202,206,30,111,178, +87,199,189,241,93,36,63,225,25,219,51,13,87,107,72,135,58,168,202,132,229,145,172,86,33,34,102,18,223,199,197,161,47,105,100,77,84,224,29,45,0,150,58,140,99,225,214,237,169,197,79,110,168,192,215,198,193,247,58,230,240,249,27,65,154,177,253,140,232,215, +165,184,29,192,45,213,33,212,178,24,187,26,77,48,222,161,141,84,34,147,237,156,162,147,63,168,162,136,233,135,178,224,136,66,22,142,239,244,110,234,197,206,134,230,236,103,191,83,200,97,93,140,59,90,204,246,118,74,81,254,210,102,31,14,87,69,240,203,10, +10,86,40,54,101,36,1,180,118,232,125,44,87,211,248,77,132,54,112,14,204,252,239,38,3,157,229,67,196,138,138,116,177,28,156,112,22,147,94,137,165,80,76,67,17,157,192,126,10,87,102,44,21,82,81,36,117,247,240,182,251,59,106,112,176,151,221,252,133,191,253, +177,193,96,212,53,96,202,57,83,2,75,221,146,71,23,45,161,214,177,90,127,212,43,244,153,103,210,188,214,44,16,224,143,85,119,191,52,221,29,163,16,187,154,153,249,84,172,36,251,92,35,83,71,178,60,214,243,139,45,196,216,214,229,120,207,108,84,234,227,143, +76,185,214,108,82,146,170,6,80,2,216,49,228,151,113,61,100,39,128,98,69,108,150,177,151,199,151,204,16,165,211,41,151,62,240,145,170,69,41,94,177,4,120,245,219,11,199,151,31,16,241,149,130,252,224,106,114,154,100,229,7,149,112,120,73,32,49,177,0,228, +156,221,184,48,195,57,194,6,224,56,95,149,136,68,246,222,174,159,174,122,84,174,180,144,229,40,183,76,219,125,234,51,161,84,81,80,30,28,193,191,182,118,69,189,125,251,151,119,8,118,114,248,209,4,213,4,176,201,130,56,239,255,20,205,212,144,152,222,254, +179,210,18,60,49,31,139,111,43,218,219,174,215,165,61,144,215,169,98,15,223,113,12,18,212,89,206,242,150,230,96,134,0,156,235,29,73,21,160,229,121,147,31,142,53,109,136,250,147,185,155,79,127,106,161,31,215,17,160,176,247,20,116,35,56,98,203,236,11,223, +24,150,21,39,29,44,17,48,200,237,151,251,1,53,89,194,149,204,83,6,69,102,212,247,94,226,91,70,162,85,35,152,182,39,158,105,148,236,18,34,159,154,164,145,59,190,56,85,146,39,102,133,69,90,143,221,104,0,158,185,95,232,153,227,45,88,29,198,193,129,175,84, +167,78,179,22,86,25,73,40,56,207,163,72,178,65,126,167,242,74,123,106,233,227,255,78,123,130,177,234,203,44,196,244,102,85,236,90,64,208,108,249,24,225,75,231,240,175,178,232,225,106,24,191,114,62,20,85,129,213,49,176,252,142,147,238,51,153,199,184,39, +123,17,112,23,146,169,70,33,173,30,183,111,12,228,181,91,139,93,183,177,113,223,110,188,176,230,222,77,134,239,64,197,82,26,115,137,83,175,151,245,218,198,154,155,200,149,53,79,127,12,100,204,173,73,88,49,71,238,178,9,165,72,145,51,100,182,159,249,216, +239,237,116,221,241,70,67,166,132,242,139,134,100,80,226,216,62,100,159,240,67,234,145,178,21,176,96,175,105,18,49,103,73,120,33,165,71,140,89,221,215,191,98,255,243,221,205,78,254,228,39,142,229,206,20,82,227,59,98,25,61,158,52,198,95,152,96,77,33,15, +7,133,218,187,95,234,206,22,117,46,212,39,159,8,93,92,131,73,238,102,212,54,39,133,106,21,111,255,157,255,210,15,16,135,227,179,52,5,135,69,164,44,242,222,244,213,12,174,97,138,99,240,198,145,8,158,32,41,138,56,217,175,148,248,38,36,157,84,37,144,116, +168,30,206,35,117,135,103,209,157,126,244,83,55,172,244,108,6,126,97,171,192,74,107,60,167,108,26,78,111,115,76,230,192,112,101,30,80,226,50,30,226,87,100,113,201,57,171,156,207,200,215,177,10,37,141,56,213,124,95,117,183,219,246,17,88,197,215,254,242, +142,163,182,114,19,12,30,135,179,142,57,3,106,237,138,204,129,122,95,193,42,53,169,82,188,164,54,32,84,58,216,43,85,227,206,188,90,182,28,77,160,212,105,197,217,247,253,150,231,47,17,131,218,52,225,96,72,229,154,200,4,112,84,123,10,111,79,142,140,62, +118,50,137,84,0,20,199,230,100,136,244,51,146,78,57,126,112,46,241,155,43,115,200,24,94,56,99,77,174,43,187,187,170,127,231,118,240,141,24,203,247,220,143,207,64,163,193,81,35,22,165,112,109,89,50,174,199,253,77,124,50,66,6,22,163,67,208,236,193,201, +199,72,1,20,99,112,182,120,254,48,151,148,155,173,168,191,131,123,90,125,243,247,246,195,24,200,185,209,74,132,59,138,18,18,159,218,215,36,67,154,245,70,84,208,244,51,231,75,222,24,0,202,213,235,169,36,31,88,103,204,145,21,82,7,165,125,242,215,85,92, +241,220,204,194,58,204,240,125,56,110,58,12,164,225,56,26,202,9,50,6,211,101,249,187,83,114,188,16,46,175,211,61,101,35,111,209,242,138,122,37,232,36,235,228,147,236,100,157,160,189,96,165,182,246,114,228,93,23,21,233,68,238,224,69,51,44,253,83,56,174, +254,246,107,150,61,178,151,47,175,77,24,237,119,23,236,164,152,109,34,220,201,154,100,175,8,167,196,112,91,176,157,123,202,189,92,149,158,14,192,23,79,230,13,243,203,180,90,126,92,59,137,108,114,159,181,90,170,250,102,88,114,142,11,231,17,106,153,159, +228,57,99,250,34,250,90,112,199,118,66,152,27,153,89,88,138,146,221,250,118,251,63,252,212,47,60,241,224,137,167,155,195,31,255,185,153,114,52,238,153,71,159,108,113,6,177,59,177,148,210,40,154,159,127,225,248,120,250,68,110,167,216,87,26,23,238,238, +237,62,75,38,220,83,207,214,240,118,88,105,48,122,4,50,210,36,43,189,57,161,68,40,253,222,144,168,216,177,207,149,157,40,32,230,25,214,216,138,77,82,53,152,230,102,171,102,57,231,105,52,202,125,245,132,243,237,61,2,171,39,25,29,175,154,238,217,159,248, +141,127,1,7,108,169,216,236,192,195,50,229,228,68,241,173,92,208,182,192,50,238,59,73,245,150,50,238,143,24,1,222,41,73,68,184,52,199,131,65,95,222,142,209,5,185,247,169,169,41,228,117,58,170,47,181,219,165,19,58,177,58,22,207,212,244,141,139,219,110, +229,226,188,104,108,21,199,190,127,18,96,82,87,236,181,245,138,97,93,49,111,94,127,219,243,66,32,99,22,140,15,111,67,45,246,246,20,171,20,185,237,9,135,51,179,174,152,166,165,200,135,142,136,143,25,12,59,32,65,65,225,210,191,112,18,90,40,102,139,185, +128,128,195,94,168,92,25,139,230,99,206,217,41,247,59,106,144,68,115,87,63,187,250,200,193,103,127,254,120,231,103,254,246,241,97,86,244,213,241,83,169,105,76,27,176,70,200,114,61,232,25,100,84,130,205,215,187,230,212,185,154,8,248,96,167,48,183,55,70, +38,105,24,175,177,136,47,200,7,28,63,29,186,159,254,187,83,57,229,211,108,165,212,60,1,73,5,139,122,141,31,169,3,112,100,64,129,147,59,164,0,164,135,135,113,108,16,50,171,100,164,78,214,190,109,66,203,210,210,185,47,45,30,95,185,199,193,69,88,42,150, +130,231,107,144,71,144,1,96,145,79,233,200,115,9,154,194,74,99,195,58,180,24,105,29,182,83,22,249,192,53,109,77,218,41,253,78,50,109,117,176,107,173,236,53,128,24,182,124,16,153,25,184,192,221,102,230,56,103,145,26,75,161,178,60,70,186,218,47,168,170, +15,229,125,242,177,106,178,14,69,178,107,106,123,89,57,90,227,153,25,222,199,88,246,166,42,122,75,86,45,46,136,198,242,201,117,142,88,24,182,196,140,112,115,4,99,185,218,113,6,216,251,105,19,152,106,14,147,28,121,141,133,95,30,144,65,35,242,97,117,74, +1,79,148,59,97,111,217,228,22,34,224,45,137,59,0,164,190,242,127,216,255,128,240,102,115,231,65,55,124,252,169,153,209,99,79,213,179,227,75,193,232,205,239,117,130,83,31,107,23,223,123,237,129,186,245,23,251,154,118,254,213,63,31,152,189,123,165,196, +157,68,188,124,199,99,143,66,19,27,165,93,124,82,149,237,121,144,122,236,174,33,231,228,123,184,164,246,1,104,152,69,133,242,59,216,169,2,42,20,190,59,211,221,253,161,33,10,54,50,131,79,58,222,97,162,15,119,40,19,83,150,36,201,253,95,252,185,127,114, +153,211,193,74,142,118,204,16,193,224,187,23,128,212,163,33,121,96,63,190,32,22,215,227,164,6,154,11,187,156,196,175,169,215,86,14,182,238,87,53,10,164,18,185,119,144,140,9,74,172,107,130,8,34,88,229,99,159,174,146,48,159,251,245,243,238,57,234,30,107, +138,47,42,67,18,201,39,153,222,39,31,91,29,220,49,210,49,29,196,237,67,86,252,224,46,203,116,209,28,204,49,133,187,181,185,165,56,7,240,216,83,76,5,207,168,244,56,136,10,58,253,102,245,198,53,156,142,57,41,165,64,230,134,167,57,1,87,155,41,254,7,22,86, +102,54,16,84,16,41,58,41,122,161,153,102,23,11,135,117,200,92,60,72,66,217,255,244,175,244,63,59,249,200,244,254,55,255,224,181,244,211,171,143,13,166,142,199,250,147,63,51,15,100,44,173,3,1,173,44,71,188,12,123,5,88,40,163,23,158,104,232,153,71,180, +157,62,25,150,143,60,97,178,95,250,135,179,227,103,126,166,53,126,226,153,250,72,134,23,248,34,23,235,219,239,164,195,219,19,130,94,99,129,60,71,38,12,195,128,220,49,231,70,136,9,102,55,78,233,59,61,84,197,249,3,156,245,87,127,246,31,93,146,57,107,156, +66,195,248,21,32,48,144,150,65,44,152,20,177,124,148,75,238,121,144,201,0,18,155,143,199,150,86,107,128,248,149,183,9,156,136,136,155,22,217,164,168,46,231,206,78,7,38,141,219,110,178,107,23,143,62,92,31,39,182,245,4,52,45,203,125,178,159,145,239,214, +176,27,43,151,222,129,140,223,71,99,181,227,74,152,164,134,88,56,229,31,218,80,159,226,182,157,217,216,246,15,182,220,95,108,149,126,254,61,130,174,233,150,181,61,187,111,243,168,42,213,232,247,44,145,113,81,141,129,5,107,239,252,138,205,4,92,4,9,23, +89,104,15,227,60,14,48,200,217,208,195,47,134,208,7,198,233,205,87,237,131,205,215,210,127,245,27,127,255,199,14,192,0,142,62,241,236,137,131,56,181,57,78,182,89,254,212,137,209,227,207,180,237,184,24,193,103,229,156,25,130,19,12,12,130,219,59,247,14, +212,75,223,232,196,131,46,200,201,81,81,133,232,18,127,86,185,243,10,250,248,8,73,232,36,222,158,158,74,139,40,13,114,206,11,227,217,25,15,225,25,160,185,0,83,218,151,81,248,89,7,31,95,126,238,197,165,211,43,247,56,120,196,201,228,34,203,161,185,12,0, +44,126,1,120,202,208,50,196,97,49,27,149,30,230,216,242,91,91,51,246,85,116,174,242,175,135,91,197,244,21,39,148,231,131,122,57,134,143,229,57,101,212,193,237,93,56,200,155,109,53,156,210,118,102,243,8,117,40,131,193,31,110,155,118,84,128,239,40,102, +155,104,44,7,120,61,7,255,250,121,229,55,24,228,177,172,188,49,230,80,169,229,159,60,29,220,121,67,57,217,196,243,110,105,103,231,248,12,4,127,182,105,163,121,21,244,190,107,225,131,235,102,204,68,120,134,132,65,1,140,28,112,216,4,204,173,97,235,131, +15,127,66,233,154,16,194,208,90,153,166,71,118,30,56,185,224,28,15,58,96,101,214,127,87,125,243,111,53,131,185,197,199,213,47,242,177,229,79,28,63,128,6,153,238,190,14,151,206,70,249,99,103,27,193,141,63,122,160,78,158,154,51,156,20,53,183,128,147,88, +102,201,226,82,35,99,189,20,66,15,132,46,19,58,213,74,201,158,160,219,170,82,72,85,143,176,1,90,84,186,244,247,3,89,195,233,130,182,236,33,91,8,191,9,70,77,38,26,44,45,126,236,202,103,62,243,194,31,225,59,151,196,253,48,216,136,145,67,177,60,196,77,156, +17,23,196,88,161,220,209,35,8,69,91,99,164,226,56,188,11,24,194,54,27,49,199,2,177,195,0,25,159,132,46,205,206,5,13,55,96,34,5,120,100,26,41,198,238,97,14,27,49,43,190,252,28,252,107,242,245,101,247,229,150,114,203,29,169,30,85,208,212,74,192,23,100, +47,188,163,204,225,59,226,159,106,93,11,239,72,225,50,125,183,182,182,172,159,159,57,99,252,86,160,62,225,190,251,157,80,183,231,125,114,156,157,237,220,221,202,100,136,89,186,48,140,117,19,244,238,194,71,44,14,12,200,163,32,75,97,228,216,70,105,101, +102,169,225,222,145,108,196,42,199,38,224,92,96,91,74,31,158,225,140,68,68,185,17,59,194,117,213,250,207,6,45,214,159,114,74,204,223,252,251,238,151,23,31,83,191,180,189,213,13,103,143,181,237,160,7,181,96,47,12,22,193,238,118,86,251,246,215,55,205,210, +99,243,172,6,181,51,199,146,114,136,16,233,165,111,190,25,159,92,154,178,199,142,213,114,254,112,210,144,32,230,153,205,81,99,240,119,249,184,64,218,142,172,85,207,140,179,28,73,128,161,25,141,134,122,127,175,167,239,221,223,11,7,253,33,140,72,174,251, +253,158,30,102,3,189,180,244,196,151,254,214,207,255,119,255,39,160,125,206,173,36,0,22,10,21,8,113,198,73,26,50,34,40,14,252,44,10,55,176,185,184,155,0,9,85,196,98,22,86,128,105,42,90,177,184,30,151,12,113,91,160,222,68,37,108,221,238,13,240,107,96, +245,88,196,54,217,121,43,40,115,75,98,66,245,79,219,205,25,229,254,201,243,74,223,223,240,233,186,85,172,210,23,110,248,25,33,111,223,116,233,125,135,100,122,213,189,160,56,170,224,106,231,53,187,176,57,172,182,47,203,220,99,171,167,213,217,149,64,115, +216,52,167,135,143,122,126,107,176,46,190,212,96,127,223,54,79,42,153,235,31,135,125,97,87,56,33,133,219,160,48,248,176,58,42,57,112,99,98,162,100,78,82,226,205,49,68,110,153,125,167,121,147,41,222,5,59,43,77,206,209,34,255,241,159,155,223,123,233,143, +244,255,142,16,105,112,227,79,239,196,32,40,130,157,187,93,93,175,39,69,163,25,151,208,48,253,205,107,111,70,49,8,217,233,185,52,63,54,95,207,127,230,23,206,14,78,61,62,61,70,76,76,127,41,168,213,149,71,42,150,170,58,23,87,217,233,2,153,239,97,159,131, +41,116,152,36,1,83,126,64,248,44,73,213,131,143,157,251,204,139,191,184,250,79,254,121,105,240,53,149,76,0,43,227,136,248,0,43,21,84,24,200,8,118,110,243,201,150,99,145,101,76,125,24,217,132,99,251,0,22,137,134,217,210,193,105,228,132,100,51,88,150,28, +49,79,66,103,192,13,154,202,93,203,52,104,3,108,19,195,73,214,150,77,102,80,72,73,12,66,147,63,128,182,210,130,190,120,195,215,124,115,114,30,133,250,246,68,159,126,31,193,138,208,185,71,221,252,198,186,228,101,165,35,96,29,166,121,190,90,16,28,7,116, +11,127,207,37,230,209,233,72,54,51,72,143,71,33,39,136,7,205,1,137,82,105,134,70,48,104,178,52,144,73,161,82,150,150,225,15,206,69,26,233,96,8,173,149,84,31,199,1,21,126,28,144,104,107,200,105,180,58,200,11,63,73,70,6,99,114,58,27,212,123,233,81,117, +236,39,126,97,252,223,226,29,103,254,243,191,127,181,245,179,127,115,121,248,250,95,110,71,227,161,142,79,157,158,30,246,186,67,89,74,33,12,10,17,50,247,35,28,143,139,160,0,129,145,81,99,199,0,53,72,186,83,99,113,17,20,60,236,143,244,96,56,50,221,131, +190,217,219,239,27,54,151,245,122,131,96,231,193,1,210,60,225,173,103,63,253,155,255,211,194,137,115,119,233,83,177,64,65,162,154,130,220,18,115,84,16,106,33,190,148,166,56,176,156,23,40,99,128,56,215,120,50,14,40,131,224,240,79,217,108,112,141,68,57, +205,50,181,149,130,237,67,239,167,27,13,219,117,15,181,149,137,245,20,218,58,25,76,77,42,209,239,21,112,93,205,92,61,111,253,78,217,111,157,91,172,212,195,73,168,223,87,176,50,255,191,106,126,230,155,93,239,40,35,59,117,28,153,243,196,61,220,90,8,125, +12,55,212,69,182,167,41,237,221,198,32,61,35,131,51,58,57,123,36,217,225,158,5,118,92,147,105,167,20,50,59,220,35,228,212,56,215,63,148,57,197,176,82,69,44,147,69,33,96,19,165,58,146,13,27,112,59,147,177,74,90,182,134,4,90,14,56,135,248,231,127,51,255, +229,147,167,130,223,120,233,155,91,181,211,79,206,14,175,254,203,239,182,143,157,104,150,244,93,155,223,235,199,51,115,53,199,166,132,246,108,152,167,181,160,40,104,24,45,83,140,16,44,114,197,200,201,178,248,92,81,168,125,228,33,59,187,189,112,52,28, +81,160,102,119,183,27,116,187,195,241,212,236,201,175,124,246,217,127,112,185,0,181,201,172,27,146,4,101,14,134,44,231,244,19,86,248,192,193,112,11,32,167,189,96,97,156,11,114,226,36,249,145,170,203,45,199,168,146,128,32,103,38,93,237,68,200,214,198, +38,17,94,84,70,1,69,13,188,184,83,6,237,150,109,128,164,217,191,143,144,7,160,137,109,177,164,111,251,48,197,221,123,133,123,233,209,125,123,94,38,186,251,190,157,13,191,15,207,97,145,255,209,180,93,160,222,199,12,139,198,114,224,156,79,197,170,109,0, +135,151,90,74,247,106,243,220,130,69,205,206,105,117,247,65,160,27,179,165,235,182,88,70,226,41,178,144,197,224,248,91,131,42,62,216,142,85,138,191,86,250,118,66,53,42,70,50,176,35,137,164,251,140,204,189,98,123,59,126,180,142,25,223,24,63,89,141,212, +77,204,118,97,152,175,82,200,120,91,133,224,36,169,100,18,184,123,253,21,243,221,7,119,213,31,157,121,42,53,205,134,62,241,200,227,109,38,95,57,215,31,80,186,31,238,220,129,239,220,181,102,235,141,126,52,26,23,81,41,201,97,82,195,8,149,153,53,2,253,67, +130,98,156,145,127,206,161,181,99,13,97,154,253,206,40,139,107,199,254,237,249,207,254,221,255,249,212,163,159,250,22,150,17,33,113,193,33,174,185,165,95,117,69,28,115,103,82,102,222,180,184,140,24,244,164,21,70,49,18,194,137,154,138,149,90,214,146,4, +190,133,144,153,233,185,132,6,172,148,209,243,141,182,157,117,145,240,143,128,140,182,57,155,168,238,192,218,221,7,120,115,112,218,181,94,193,169,229,106,80,95,178,167,146,7,234,102,150,185,115,181,66,109,233,174,8,111,126,251,180,250,60,204,245,197, +175,94,124,135,80,223,83,99,143,78,142,193,159,163,213,112,138,229,24,95,252,237,117,63,67,183,81,55,143,157,198,157,183,16,171,157,110,132,100,148,203,157,93,163,118,65,90,28,15,67,206,46,218,222,237,233,198,84,203,112,94,175,29,105,115,20,72,149,3, +14,83,228,222,54,204,133,27,153,77,204,217,191,210,206,95,176,156,168,148,25,79,94,87,53,119,109,13,57,225,180,100,241,47,83,166,162,185,161,12,34,57,126,178,60,246,200,41,251,196,241,197,114,181,223,207,158,184,125,179,147,180,90,156,128,230,184,209, +160,137,107,44,56,71,94,126,4,40,36,121,92,111,138,123,7,67,61,28,229,65,247,96,96,250,131,242,102,191,31,188,252,232,169,159,248,247,58,108,245,184,0,180,51,190,29,203,72,20,43,116,33,181,150,108,133,83,30,7,8,80,146,113,124,0,77,98,146,173,220,175, +57,184,139,205,177,113,194,65,108,5,177,6,76,181,96,238,52,76,203,190,204,81,108,202,152,91,96,65,217,85,139,59,106,249,237,74,89,109,183,96,121,114,23,190,115,186,84,191,69,77,93,119,236,213,17,30,191,58,30,214,19,127,0,193,78,132,203,216,232,242,229, +11,178,37,8,39,180,77,70,189,113,66,219,12,16,178,236,46,121,83,41,153,89,188,147,24,22,145,207,226,244,203,230,185,144,213,248,101,231,134,237,126,192,13,121,57,73,134,99,204,250,244,183,12,211,169,153,99,109,234,49,167,182,165,156,120,25,112,154,56, +7,80,155,16,161,108,1,108,236,252,54,44,134,169,106,153,232,13,81,114,106,91,201,113,140,37,19,215,70,8,65,25,115,106,57,160,144,13,65,230,196,241,242,216,177,133,236,108,154,22,167,16,27,47,34,174,154,3,19,112,140,177,40,16,49,209,240,16,153,149,97, +54,214,119,32,244,206,221,173,209,157,246,244,199,190,85,218,168,207,40,140,224,71,102,29,195,219,9,215,76,237,35,236,117,156,251,231,56,130,149,255,122,211,43,123,71,100,200,62,32,238,197,117,206,46,78,234,145,29,2,243,114,102,113,100,98,248,221,145, +203,108,132,156,32,132,13,206,152,66,37,67,151,196,156,198,182,111,79,204,183,220,206,27,120,218,148,247,173,172,33,238,87,19,217,56,249,116,25,196,144,186,177,44,11,130,100,196,100,151,50,47,217,138,51,248,48,26,235,181,214,39,5,124,145,219,154,34,11, +181,9,193,46,118,106,154,51,41,238,252,228,157,224,224,219,75,174,81,222,147,125,235,30,175,178,62,97,43,12,6,247,216,171,194,105,123,125,51,222,243,181,199,65,58,14,6,84,208,146,27,254,114,7,20,68,128,131,20,154,155,5,28,27,79,225,234,58,195,30,101, +198,102,20,154,113,196,25,13,80,104,14,204,242,115,137,11,6,22,108,191,148,73,252,44,192,16,215,5,50,40,48,227,65,233,219,42,76,88,117,25,148,210,115,75,196,93,142,113,27,217,29,153,166,38,191,147,157,83,126,32,181,140,96,151,137,48,74,82,169,36,245, +153,115,147,183,87,172,125,40,132,246,140,130,64,72,7,86,186,200,76,79,50,103,67,121,71,96,110,14,31,10,189,31,141,8,150,98,176,55,174,36,9,1,170,144,248,222,202,222,180,81,207,49,137,211,9,138,178,6,9,6,45,191,69,26,35,11,18,18,51,141,5,183,209,185, +229,100,134,34,184,97,169,35,86,94,176,50,93,156,237,173,27,126,87,143,247,26,107,251,1,246,221,185,192,118,15,189,86,109,231,204,253,195,63,183,120,222,169,149,101,203,81,153,225,235,75,150,25,159,77,100,248,159,196,151,19,52,7,226,122,124,199,185,228, +164,236,155,98,123,251,12,225,248,227,234,0,136,32,224,1,3,57,113,27,92,168,229,138,14,103,240,131,106,202,19,228,149,25,235,65,11,202,177,245,160,36,150,250,9,49,117,50,140,210,151,196,177,136,162,40,93,238,159,163,116,142,44,127,22,70,176,231,8,148, +113,98,11,46,1,60,63,199,58,202,40,28,147,24,208,178,220,143,73,58,28,51,129,116,160,106,225,28,16,223,24,192,28,144,13,220,204,39,144,221,3,64,28,69,192,64,65,30,38,178,77,142,196,169,142,251,171,197,126,32,166,180,72,86,154,10,68,84,214,17,167,38,17, +175,199,28,240,227,202,134,79,162,235,36,181,113,85,37,81,140,123,46,223,129,37,203,107,197,52,123,158,170,100,58,133,202,115,71,250,144,123,22,81,168,68,195,11,191,59,244,155,25,174,109,4,19,13,101,42,149,66,229,118,164,239,181,113,225,187,10,246,136, +244,133,178,90,126,225,194,100,24,163,32,99,217,81,239,240,184,41,151,169,42,1,79,136,222,170,227,139,131,192,230,222,50,227,164,227,82,14,27,153,117,194,133,154,58,80,96,43,41,115,252,224,81,25,23,28,82,11,54,176,28,244,108,217,47,163,34,199,137,225, +64,73,238,80,133,245,47,179,128,93,229,183,104,250,24,39,241,164,210,215,69,1,5,204,19,108,184,83,128,159,68,234,119,201,1,104,133,224,32,68,19,198,204,211,148,220,77,2,177,37,135,175,113,56,30,135,94,0,189,114,84,50,132,136,215,4,68,185,16,174,204,108, +146,247,132,55,39,189,137,207,204,71,54,199,155,9,241,224,98,152,224,56,244,38,152,161,12,22,32,129,82,2,35,76,36,156,179,114,36,30,50,84,199,251,12,176,128,193,58,236,245,75,110,50,236,71,198,91,59,85,231,206,209,7,160,43,59,110,63,47,236,100,35,97, +142,216,107,128,27,8,79,42,203,237,89,24,226,108,33,69,199,105,2,243,27,80,164,21,14,45,189,34,5,134,146,92,95,243,187,110,191,219,102,15,239,233,99,39,199,145,129,94,147,50,71,181,190,174,12,119,201,146,201,170,243,27,230,112,111,128,91,8,170,127,60, +212,201,84,18,164,0,70,108,1,228,152,219,225,248,64,239,171,41,21,129,145,138,65,90,112,47,245,58,125,48,66,160,188,59,210,41,64,88,183,231,183,105,73,140,223,74,52,21,16,149,115,223,142,128,251,51,112,62,20,25,42,191,73,18,235,176,57,70,167,240,211, +200,149,128,45,69,63,44,147,90,170,158,25,154,107,139,36,60,169,29,22,163,179,130,25,22,219,9,23,205,22,199,66,230,58,65,237,124,141,179,146,45,69,67,249,141,188,95,138,238,10,154,82,37,37,61,218,249,125,120,72,127,142,57,77,220,111,178,84,112,71,203, +72,123,208,45,219,146,194,10,153,196,19,255,76,201,53,210,148,113,150,101,113,207,96,159,17,112,105,199,200,136,49,43,198,193,211,212,86,126,38,53,246,1,20,35,44,150,124,161,224,88,73,120,115,189,74,154,94,189,138,196,250,202,26,0,212,21,61,41,128,184, +114,101,195,85,116,226,7,3,79,71,5,203,189,1,86,170,157,35,232,183,95,168,70,221,174,172,172,4,100,163,126,122,241,188,254,3,144,211,91,139,183,130,229,246,105,173,222,0,159,28,220,51,73,51,10,118,119,118,21,123,124,194,162,39,147,109,185,83,35,243,181, +245,54,155,162,125,55,188,205,178,144,229,33,54,203,67,132,150,166,77,27,25,177,167,37,211,17,236,231,216,112,7,35,191,3,22,183,37,147,239,69,33,82,239,240,140,195,93,178,232,41,33,204,80,249,25,136,50,10,12,241,18,242,163,213,102,74,190,81,138,85,145, +44,84,15,89,246,10,53,103,35,114,198,68,127,41,37,200,72,218,251,205,149,40,68,230,141,89,237,17,232,90,201,60,114,81,181,40,112,202,26,93,70,61,173,118,203,226,68,161,216,147,251,101,48,196,123,197,133,248,86,241,171,48,232,19,161,54,154,118,60,4,201, +31,239,186,189,162,45,2,132,157,176,76,205,29,198,172,48,195,201,248,172,60,54,211,81,66,70,92,169,246,111,223,184,162,100,148,4,229,80,117,35,188,235,134,74,223,87,176,228,139,57,188,130,233,59,170,232,242,141,109,55,191,50,175,183,217,34,143,15,34, +82,230,198,74,91,220,133,242,103,150,245,201,194,155,247,141,151,183,244,236,98,18,14,191,231,171,44,184,7,79,191,209,15,56,97,38,60,30,6,227,142,7,83,20,50,146,201,102,176,59,54,169,241,91,138,74,19,112,201,82,21,165,71,121,206,145,139,38,229,214,39, +73,108,74,74,62,16,108,20,60,220,254,140,90,92,104,207,108,17,52,1,253,112,70,60,59,198,57,41,205,248,153,23,178,136,140,159,129,33,101,58,80,73,47,68,144,90,145,108,19,226,2,169,122,200,165,0,77,230,80,107,238,138,37,101,43,54,150,253,0,168,217,99,75, +74,148,59,85,50,137,49,40,148,207,216,48,100,197,165,59,142,203,169,208,239,82,41,123,203,130,228,79,166,113,123,80,148,237,118,91,61,128,139,98,246,134,128,105,183,218,199,189,221,60,225,226,91,183,68,64,221,183,109,20,252,91,51,176,148,147,94,229,35, +131,167,43,7,251,174,197,108,223,87,176,149,214,26,223,236,115,65,85,66,118,156,47,68,32,69,147,192,221,40,89,198,200,78,107,238,113,135,244,83,160,216,74,127,63,52,13,238,160,187,139,211,8,63,114,156,123,168,226,232,231,253,128,195,32,155,237,192,68, +121,83,143,247,6,50,45,85,134,125,113,212,235,152,176,199,239,228,204,13,92,229,59,24,191,113,17,107,193,24,6,73,151,30,19,10,92,241,208,78,184,65,29,198,177,241,3,60,34,53,230,108,165,170,220,147,197,218,92,12,212,106,73,17,226,126,10,176,76,32,116, +216,78,100,34,156,130,148,153,12,151,106,7,231,205,45,133,43,187,83,210,228,66,67,101,161,24,111,118,51,242,191,185,55,189,97,157,117,62,4,125,137,248,84,237,0,150,240,4,34,96,206,252,103,242,156,37,165,156,219,113,240,160,176,245,147,211,46,224,140, +172,212,55,52,191,38,27,5,151,50,241,238,201,179,103,213,201,57,95,33,202,65,93,139,0,170,236,131,173,90,37,171,182,20,47,59,253,62,219,140,126,95,84,92,81,139,238,112,227,173,202,182,95,184,176,58,1,83,193,243,207,159,7,65,13,63,112,101,67,176,20,77, +138,218,84,172,45,149,221,141,183,171,226,242,12,171,52,27,208,60,53,173,238,52,97,129,157,148,144,112,53,55,107,128,252,220,132,129,123,178,68,182,224,120,117,34,103,210,112,124,45,17,51,48,111,46,9,249,164,26,201,14,255,71,77,34,104,33,160,161,77,86, +81,128,79,3,49,28,51,53,30,228,4,60,5,51,164,177,223,148,129,243,84,75,155,21,5,231,163,6,0,88,1,43,185,97,153,249,60,68,207,114,29,207,31,19,157,227,253,128,134,242,180,12,50,169,160,195,199,197,164,9,185,65,41,43,36,104,110,107,177,31,128,41,230,217, +11,149,149,35,229,176,198,93,42,108,18,121,255,233,247,110,111,137,80,73,242,79,132,74,176,249,24,210,114,97,225,133,202,222,156,141,117,70,28,247,197,191,250,83,126,229,112,18,173,199,59,218,85,35,240,223,243,248,64,62,246,232,237,163,197,201,212,94, +191,119,248,186,246,64,234,184,121,140,64,136,80,189,27,234,111,247,67,189,200,45,188,129,246,154,200,0,213,30,135,217,29,26,93,118,15,204,168,102,244,20,0,213,30,52,245,100,91,155,157,78,95,79,87,166,121,12,115,204,162,115,154,101,14,146,164,137,165, +105,150,153,139,54,3,145,3,232,83,70,110,220,203,77,163,13,98,36,171,246,145,229,96,176,60,87,147,158,91,106,169,58,236,49,70,66,144,247,243,113,195,234,186,92,6,106,177,81,172,52,126,215,201,72,197,82,26,203,130,187,178,106,115,100,243,84,152,135,101, +204,89,195,67,39,187,81,66,226,37,247,112,47,66,220,1,160,84,192,167,178,17,141,207,111,2,253,82,152,67,34,228,237,3,41,254,206,17,171,114,54,34,51,95,20,106,148,31,179,105,11,4,109,50,46,89,241,25,67,176,228,130,147,71,207,138,48,191,188,181,161,159, +83,203,150,69,132,116,117,47,222,184,226,86,46,67,139,47,93,32,10,174,74,6,222,25,187,30,61,62,64,28,251,78,129,210,113,203,84,84,209,94,175,194,11,231,90,122,109,101,89,253,225,119,118,202,201,230,192,28,207,23,220,206,109,147,53,59,243,236,34,42,185, +51,163,35,34,60,153,180,165,118,54,29,237,187,193,193,190,156,12,102,58,88,130,201,205,117,101,183,79,218,46,156,56,18,230,188,206,236,8,205,32,104,68,33,1,0,206,44,120,85,137,99,105,42,193,102,148,76,151,49,79,36,92,45,121,32,18,6,220,20,88,143,203, +160,34,234,195,234,47,183,29,96,200,2,62,36,231,109,238,149,67,218,94,242,76,150,181,174,208,216,113,144,35,92,179,172,103,162,64,35,92,152,153,25,195,228,102,145,31,75,27,143,11,232,98,202,93,21,109,50,227,196,252,82,168,236,64,164,80,235,244,227,7, +211,37,133,218,2,101,56,172,109,185,59,219,243,101,127,106,9,239,179,80,222,70,174,53,185,127,214,206,44,110,232,47,111,41,183,176,48,148,212,28,195,25,252,181,76,155,94,184,114,161,74,163,78,10,4,222,255,248,64,62,86,222,80,76,178,62,172,230,146,2,170, +181,43,135,117,199,36,48,54,182,193,74,117,253,136,249,159,252,245,57,241,141,108,226,98,43,17,231,27,159,93,65,24,84,211,250,193,14,146,240,212,92,228,230,19,132,66,163,204,179,85,236,89,140,243,150,142,230,148,78,245,80,182,130,61,216,99,191,11,192, +21,71,191,114,138,89,52,217,9,26,26,51,24,203,110,84,177,201,14,23,40,203,58,33,29,157,64,251,216,55,84,11,253,132,150,88,197,111,249,61,172,191,10,234,9,171,41,164,191,40,81,210,179,110,57,190,61,105,251,231,16,24,9,218,29,130,72,193,106,27,15,7,54, +106,166,28,84,11,13,133,230,238,129,148,168,43,41,144,103,156,154,131,124,32,77,200,129,161,25,22,172,46,61,1,17,180,218,246,49,44,232,125,172,83,161,11,59,11,238,160,113,75,54,112,224,231,112,19,7,254,237,108,250,109,69,191,12,78,248,185,231,86,185, +241,242,161,15,101,220,90,213,15,79,66,155,163,254,246,29,199,7,218,102,148,135,116,112,250,54,75,230,164,57,152,218,169,141,53,105,186,93,191,244,80,243,159,95,62,79,186,11,95,82,149,113,118,43,216,64,128,176,204,137,228,75,200,14,109,22,150,19,198, +134,28,150,129,223,60,189,135,16,62,179,42,235,195,171,29,155,51,83,76,234,68,206,244,223,236,105,245,72,75,141,59,3,199,184,119,4,92,67,116,89,111,196,250,160,24,201,34,96,124,152,212,106,0,89,67,61,224,120,175,156,3,191,198,26,169,65,147,64,172,172, +67,99,149,205,72,70,75,69,135,229,251,156,58,26,214,98,23,165,177,46,213,72,58,225,232,164,243,128,93,112,74,79,205,37,16,32,204,64,196,38,0,58,206,164,76,235,44,126,231,132,255,196,113,91,7,182,196,142,238,219,114,102,138,51,166,234,28,160,162,40,84, +164,152,108,255,254,62,44,82,199,53,70,205,114,152,128,235,5,43,115,12,12,244,43,123,165,229,224,23,41,184,94,186,163,102,95,134,80,63,5,161,238,42,119,245,119,175,203,200,37,37,66,85,66,244,175,94,83,110,123,178,117,55,112,235,11,254,244,30,10,178,138, +115,222,83,94,31,216,20,87,194,117,210,213,40,102,249,146,240,148,76,196,179,68,245,69,105,233,99,8,164,202,206,130,114,231,22,21,64,194,64,246,140,97,135,30,95,79,228,55,121,47,246,215,34,151,83,114,43,146,122,60,237,216,77,192,169,218,233,200,185,233, +147,13,191,250,1,170,200,84,49,111,153,142,96,153,113,158,219,240,101,140,17,181,77,36,180,8,155,114,178,193,201,14,202,62,19,131,36,10,96,190,197,76,134,145,152,74,78,243,38,224,33,29,84,166,22,116,194,8,87,249,134,120,30,30,67,122,177,96,12,138,59, +243,33,196,60,198,251,38,53,208,137,184,77,243,202,247,173,147,112,72,173,229,166,193,200,153,202,66,19,215,129,53,195,44,13,191,66,247,118,199,166,112,51,41,231,12,207,250,22,72,178,74,209,108,41,53,216,211,79,88,71,127,58,3,19,204,221,227,94,250,103, +202,178,61,149,243,135,121,78,206,81,168,68,192,204,222,48,250,120,161,18,42,180,149,51,65,142,186,195,247,243,175,242,184,250,144,199,97,74,143,133,182,23,171,109,165,165,215,231,97,24,196,199,23,22,86,165,35,76,182,35,5,51,181,123,194,251,93,38,230, +247,193,135,62,5,234,140,19,61,153,164,39,183,60,217,9,132,241,46,39,150,115,51,166,237,157,158,111,127,200,253,22,38,236,48,144,141,133,73,254,22,236,84,243,75,150,67,38,101,177,12,135,138,225,18,213,130,197,234,12,145,162,60,117,156,87,200,82,7,254, +13,195,49,114,188,137,152,216,201,80,143,94,206,38,110,37,197,102,52,247,212,170,58,22,14,59,6,201,22,179,238,183,89,17,248,123,187,61,233,116,96,149,109,25,212,75,213,61,80,217,28,124,238,8,2,29,146,77,154,130,223,244,59,75,18,87,52,170,218,235,62,76, +241,99,21,72,58,41,37,188,213,166,102,192,37,52,193,28,83,187,60,95,69,26,151,189,64,15,19,56,111,219,9,235,251,9,149,71,160,62,228,193,226,54,36,124,204,242,231,183,245,246,246,138,218,222,190,98,230,215,55,244,154,94,113,171,248,2,219,47,158,214,219, +199,111,233,86,107,71,183,182,22,221,233,227,243,238,229,253,187,68,163,142,13,170,237,182,230,232,79,54,52,43,248,35,53,131,244,206,160,175,213,0,103,113,122,10,238,12,235,102,111,212,81,249,254,72,138,180,147,132,150,180,229,106,181,76,135,83,60,157, +136,250,77,29,185,74,72,106,232,179,241,81,129,196,66,35,87,81,45,116,41,98,26,128,90,246,6,41,250,65,141,108,248,160,143,112,132,215,155,88,149,105,225,198,253,128,91,129,2,237,2,185,194,28,52,17,135,114,236,71,28,64,120,200,0,196,224,137,18,114,212, +131,18,225,20,99,88,39,219,128,78,181,35,144,248,72,146,207,55,129,194,67,36,238,186,160,53,27,229,193,40,118,205,102,2,115,157,56,88,17,71,128,88,48,192,70,224,196,93,56,10,36,205,129,193,28,219,34,51,152,229,252,196,172,27,253,52,40,196,206,188,122, +189,133,133,213,90,84,55,250,167,221,10,119,36,67,24,115,233,26,40,219,35,102,119,117,245,131,107,234,15,33,88,167,87,241,73,95,250,210,151,36,150,229,223,249,237,207,235,47,33,155,127,237,218,37,189,190,189,141,199,127,85,157,122,125,81,49,119,123,123, +29,137,226,165,103,85,111,180,173,119,58,90,5,35,152,231,207,62,169,166,92,91,141,0,156,6,56,127,41,253,30,178,237,7,33,183,233,5,227,4,253,169,1,28,197,8,57,198,56,105,108,3,233,197,35,110,228,170,144,231,84,249,38,39,138,227,140,231,13,151,128,247, +139,226,129,26,179,105,181,172,115,47,19,58,11,215,226,136,1,64,89,182,143,192,86,184,160,1,205,219,31,128,255,77,108,29,26,24,104,41,86,119,181,216,111,199,67,234,79,215,113,157,179,12,235,153,29,176,7,9,120,107,38,102,70,106,36,147,231,220,128,141, +53,77,228,78,247,45,204,131,59,121,188,237,250,61,214,79,239,178,114,205,206,115,19,68,102,245,240,59,64,35,178,15,71,189,9,235,116,26,9,146,128,46,169,115,198,253,2,132,186,69,254,247,11,74,45,254,186,146,209,8,176,118,18,151,206,207,43,253,165,109, +57,205,52,201,250,243,191,35,66,117,31,86,168,242,92,245,3,30,23,47,94,52,135,125,62,19,128,86,109,69,74,147,76,20,247,252,243,103,196,44,115,136,53,59,176,39,219,167,113,123,82,142,65,231,196,236,147,143,223,49,119,192,45,179,11,191,172,98,94,130,140, +100,58,10,56,210,143,102,249,46,180,37,108,4,135,139,144,9,251,8,102,90,250,92,248,79,171,169,226,57,141,56,24,231,248,110,223,219,73,153,12,219,80,161,12,234,240,251,20,200,12,165,216,255,229,163,137,20,107,179,183,162,95,189,179,191,206,226,50,14,248, +32,229,105,14,172,205,105,102,231,90,142,108,153,238,192,207,2,217,167,194,247,58,183,71,147,251,148,199,12,52,187,210,142,177,200,46,201,5,123,251,214,45,153,49,44,9,115,229,83,113,63,245,244,89,25,197,47,155,34,41,159,45,91,243,189,170,130,120,39,123, +246,122,183,230,141,177,47,8,255,224,66,229,241,161,53,118,114,124,245,171,95,149,191,76,196,175,174,126,85,186,244,192,31,3,197,173,112,187,105,36,13,86,212,235,175,191,162,90,173,68,111,169,45,117,78,159,119,127,88,124,79,133,39,140,154,126,98,222, +149,175,76,187,109,72,112,62,57,48,119,110,23,140,109,85,45,145,89,19,42,175,5,102,120,31,38,13,34,33,93,119,239,14,211,36,136,127,225,254,130,7,0,51,219,82,100,164,187,48,215,41,205,107,43,65,176,209,211,195,3,72,192,52,221,112,8,173,198,253,233,56, +118,163,160,71,226,30,132,109,195,245,65,0,142,11,233,38,119,73,131,218,5,31,138,88,165,221,140,96,41,34,167,99,166,211,35,59,24,140,132,41,42,70,145,173,185,216,13,161,141,92,30,101,183,131,239,153,56,189,11,227,208,236,112,8,118,73,116,166,33,212,233, +71,181,222,27,129,236,236,112,46,195,35,54,131,107,57,190,184,167,255,226,207,51,87,215,35,142,190,113,211,72,176,47,130,119,125,243,27,126,170,218,241,227,94,75,39,9,22,178,75,32,32,44,140,160,28,23,47,174,186,175,126,117,117,114,158,213,135,57,126, +96,141,229,81,181,77,28,214,71,225,75,225,219,92,82,108,42,89,7,226,134,207,229,150,211,135,200,155,149,23,157,206,121,25,99,51,1,86,188,127,50,241,77,38,171,222,58,173,56,207,8,249,2,189,5,96,197,221,73,210,230,188,126,163,135,228,3,174,115,115,68,238, +163,199,189,8,198,131,93,206,48,214,163,177,7,94,41,72,15,154,247,36,157,214,113,218,213,140,73,219,162,209,28,142,205,57,15,109,197,22,69,38,34,14,240,31,167,182,178,115,60,29,181,132,44,97,3,55,143,125,185,142,247,27,78,154,206,166,92,88,204,0,217, +63,96,133,173,28,108,191,96,83,26,105,193,73,41,139,60,0,203,19,158,92,170,226,207,155,234,107,191,159,57,224,163,67,144,196,105,178,175,44,92,215,191,117,238,188,83,171,170,36,31,176,177,242,208,220,78,64,232,164,250,112,178,229,235,135,213,216,31,74, +176,147,227,33,159,236,143,75,213,236,178,229,53,229,227,93,32,229,27,55,86,29,169,199,231,88,191,94,29,175,44,40,125,253,11,215,213,226,153,243,48,209,55,165,16,125,178,211,229,109,160,231,133,69,191,183,27,203,48,39,59,70,49,108,160,112,135,93,248, +199,22,16,50,51,71,48,215,29,25,147,211,81,35,196,72,28,142,61,74,15,180,222,109,217,36,219,151,173,78,246,56,105,76,77,171,228,228,129,30,223,181,142,155,41,140,129,138,166,167,253,164,135,238,171,96,141,150,252,251,140,250,16,118,99,79,215,241,94,172, +110,224,227,205,83,199,156,186,207,158,224,123,142,37,64,159,56,34,80,238,147,64,22,137,207,99,251,11,127,11,211,110,204,120,177,145,141,169,77,198,169,175,93,61,111,73,17,50,60,164,240,46,168,135,200,151,71,181,49,210,132,132,80,74,169,15,37,204,163, +199,135,138,99,63,232,113,193,85,254,97,205,151,111,76,182,158,134,105,17,254,147,43,86,246,140,193,177,216,2,204,95,33,251,114,86,118,9,225,193,253,101,118,222,232,151,91,192,153,143,45,46,88,158,188,96,148,219,143,61,113,66,42,248,134,97,94,194,132, +137,214,156,156,67,46,160,91,148,35,196,196,20,4,95,31,76,91,59,238,35,195,62,93,90,178,62,81,217,182,76,106,103,230,94,217,78,155,229,35,39,0,221,194,172,156,71,120,27,237,88,123,31,233,52,198,157,123,172,18,236,20,229,60,139,202,194,86,185,123,107, +92,48,87,218,60,229,220,246,245,45,59,78,16,225,84,37,64,76,136,199,248,110,244,159,119,145,24,239,238,40,199,203,108,207,11,85,126,200,246,125,252,93,182,156,23,209,61,215,117,28,245,78,161,78,206,211,164,94,91,102,250,95,196,237,203,202,30,33,247,127, +96,161,242,248,72,52,118,114,76,70,246,189,240,194,149,128,153,126,17,238,197,11,178,171,165,108,163,134,36,241,5,191,39,173,234,60,127,221,176,240,153,175,155,236,116,249,232,120,202,252,235,127,118,214,2,45,234,77,36,238,159,71,26,240,238,55,110,74, +117,6,133,77,192,149,169,68,118,180,144,215,41,223,98,200,235,199,42,224,69,141,86,236,182,255,46,98,202,234,58,77,56,146,225,242,188,238,224,173,179,245,217,74,177,125,223,95,7,54,80,61,102,93,148,116,136,138,70,202,3,176,28,253,125,255,57,212,206,187, +85,133,3,167,184,223,172,222,103,247,238,134,52,36,115,18,123,50,102,97,183,223,27,251,234,85,210,130,87,0,144,214,216,72,46,212,171,20,164,85,241,63,181,20,119,87,61,127,31,14,249,190,223,241,87,161,177,218,111,10,79,94,249,194,225,186,67,78,126,178, +245,180,220,227,133,186,42,197,113,231,182,206,59,142,65,255,227,221,179,229,226,175,111,104,14,167,58,127,228,13,125,226,217,191,46,22,45,246,154,44,233,65,158,233,165,37,17,66,112,140,101,37,133,29,80,136,39,72,92,20,118,190,61,95,82,168,67,48,12,211, +72,102,243,62,150,160,204,39,243,37,181,95,237,227,111,215,223,110,35,104,162,80,137,108,31,32,121,193,69,195,177,12,47,125,99,161,60,184,179,84,122,237,36,139,118,83,157,132,32,255,24,23,230,78,119,191,163,202,133,231,151,221,43,176,62,20,170,215,208, +117,119,245,234,21,75,161,17,244,110,108,92,114,60,47,116,77,212,84,225,218,47,92,146,173,210,201,13,124,228,66,80,31,241,113,184,163,165,159,197,109,160,189,66,94,179,156,102,226,63,228,71,41,214,200,74,239,137,90,95,95,247,53,84,114,172,30,190,215, +194,166,210,204,120,244,85,221,252,225,239,238,148,212,6,234,201,68,200,143,173,220,146,247,225,140,199,103,0,188,182,153,112,184,195,124,176,15,47,188,150,43,77,19,89,132,119,12,169,60,94,231,125,242,1,8,71,78,224,57,47,227,49,217,85,153,172,208,83, +169,217,248,86,225,118,106,133,99,126,84,198,200,190,122,244,23,122,29,37,30,120,110,101,217,178,176,111,251,69,143,114,177,32,53,139,186,233,122,164,156,5,241,140,76,106,87,222,151,226,169,230,26,80,239,242,101,225,128,223,158,56,255,200,180,149,199, +71,174,177,213,151,147,233,130,132,232,236,4,35,82,231,202,156,32,118,233,148,175,144,32,239,219,222,222,246,60,243,69,110,231,181,46,1,59,47,51,157,43,182,179,176,236,254,112,115,167,60,255,91,136,59,17,3,178,192,107,115,230,186,83,219,202,54,20,82, +103,155,167,75,206,101,104,225,47,181,105,73,29,148,220,31,72,114,194,18,43,223,148,207,9,69,211,110,85,241,51,62,27,66,229,115,238,225,246,210,157,131,242,211,172,51,186,191,111,55,112,125,246,153,194,253,202,175,156,21,173,36,73,159,60,173,44,181,146, +19,71,95,130,70,202,56,158,149,251,22,168,95,173,86,25,152,229,229,43,238,220,57,126,111,210,130,107,50,117,71,90,27,47,120,179,75,11,198,20,28,221,82,37,84,30,78,85,163,242,62,74,161,138,28,212,95,221,161,39,112,221,183,220,248,59,57,176,100,210,162, +176,60,217,99,124,210,191,171,170,58,141,11,190,203,143,87,55,54,214,15,119,155,166,249,246,215,207,203,76,12,142,79,248,181,133,183,254,6,191,83,227,245,195,113,247,147,131,104,251,165,175,239,91,78,92,161,47,228,95,249,14,43,28,217,235,91,67,169,129, +156,142,67,2,129,143,17,181,147,152,167,49,161,54,146,100,153,52,69,201,215,188,176,202,223,35,181,95,210,253,38,153,152,117,16,55,50,96,84,246,183,39,174,224,100,23,142,199,195,253,71,5,170,14,103,227,170,143,86,91,229,253,212,95,211,113,52,229,119, +244,126,191,207,15,171,218,17,36,93,186,112,120,255,68,232,28,173,75,13,120,184,83,188,167,105,184,209,193,115,188,82,153,66,63,221,102,93,209,148,19,121,19,156,113,1,108,125,113,221,177,8,128,36,59,183,13,219,250,98,87,110,179,69,165,83,109,254,199, +131,241,229,106,53,72,133,199,252,6,219,89,30,150,80,123,79,177,238,191,27,71,253,34,92,121,238,185,117,3,122,213,202,246,24,23,149,44,72,186,158,53,95,91,175,94,184,252,66,233,170,200,229,162,187,164,47,168,11,246,253,82,109,31,229,241,215,38,88,30, +147,64,155,113,239,145,197,42,135,207,16,249,9,99,87,174,176,149,196,247,126,250,132,190,220,171,56,94,174,138,141,137,38,197,143,145,185,225,89,95,157,236,59,195,114,157,195,119,245,215,230,231,215,228,125,145,176,112,220,115,158,73,236,86,235,186,254, +242,151,187,146,208,6,26,150,13,0,231,63,47,211,186,237,100,71,100,89,92,248,30,236,26,191,60,97,135,38,45,165,4,65,36,19,160,165,55,94,240,143,107,128,160,139,30,32,170,11,23,14,23,233,91,52,145,12,29,232,88,171,254,138,143,191,146,56,246,189,142,195, +166,92,169,174,123,171,95,145,249,141,48,209,27,27,27,142,254,248,70,181,253,26,125,49,181,99,237,133,53,241,79,190,41,73,149,43,213,253,32,201,203,85,229,133,193,19,253,2,17,11,46,243,243,235,208,156,53,241,119,215,174,173,123,225,201,177,106,41,188, +127,250,79,207,91,154,70,10,91,214,205,218,21,17,252,197,11,19,215,112,73,62,155,87,87,170,240,68,201,140,73,95,49,200,251,233,43,15,113,255,5,169,202,159,8,181,26,120,161,15,199,7,76,46,127,29,66,253,127,204,33,179,69,156,108,206,106,170,235,154,245, +204,71,238,63,122,123,114,93,46,107,107,151,131,201,237,203,151,253,245,203,151,93,0,153,6,126,134,6,46,234,225,125,188,48,153,51,121,156,239,55,121,143,201,109,38,56,220,197,135,159,203,247,61,250,221,142,126,143,163,223,235,232,115,38,191,75,253,232, +248,224,135,63,121,213,228,8,117,209,136,32,14,133,224,2,255,248,195,251,222,190,40,46,30,17,154,23,170,127,46,5,206,235,254,242,78,65,170,31,29,127,117,199,81,109,56,114,194,143,104,140,82,15,47,111,21,226,81,43,112,244,246,219,223,239,221,4,250,255, +54,193,254,127,98,21,30,61,233,250,97,67,139,59,250,216,36,75,242,246,215,190,219,253,71,239,251,168,195,144,31,29,63,58,126,116,252,232,248,209,241,255,155,227,255,6,74,236,217,76,222,25,217,71,0,0,0,0,73,69,78,68,174,66,96,130,0,0 }; + +const char* logo_png = (const char*) temp_binary_data_10; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData12.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData12.cpp new file mode 100644 index 00000000..5557b4f3 --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData12.cpp @@ -0,0 +1,32 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== move-off.png ================== +static const unsigned char temp_binary_data_11[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,48,0,0,0,48,8,6,0,0,0,87,2,249,135,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,3,218,73,68,65,84,120, +1,213,153,59,76,219,64,24,199,191,239,28,67,37,50,148,34,133,153,206,101,71,170,84,137,215,202,206,86,9,150,46,144,240,46,25,58,5,154,208,146,192,210,133,165,93,216,179,33,202,107,168,196,142,170,78,157,58,129,4,116,104,212,38,177,239,122,159,75,130, +3,231,16,59,103,112,127,83,252,138,255,159,239,190,215,29,64,136,204,231,15,199,23,10,71,99,16,34,12,66,98,113,253,168,159,129,24,71,193,39,23,215,119,251,33,36,66,49,96,238,195,151,4,26,98,234,250,45,177,229,229,194,231,94,8,1,237,6,144,248,88,165,154, +17,92,92,11,22,208,101,3,203,204,173,237,116,129,102,180,27,96,148,171,211,13,226,107,8,76,24,102,231,50,104,6,65,35,228,180,52,239,155,221,195,25,20,215,166,7,183,154,221,51,247,105,167,171,227,194,136,219,220,76,8,97,39,232,92,110,118,104,79,117,175, +86,3,220,44,230,15,138,238,227,108,106,176,105,52,90,120,191,59,192,152,57,33,80,196,105,202,53,92,68,40,101,147,131,202,15,19,90,20,242,75,110,118,244,24,192,222,191,37,158,64,56,245,122,46,50,6,16,86,143,85,164,175,125,243,60,19,88,242,122,38,50,6, +208,188,55,46,59,50,170,17,224,0,209,54,160,46,158,195,83,213,117,20,226,204,235,217,7,55,192,83,60,138,51,4,126,21,8,188,167,80,12,66,226,174,168,67,52,19,111,0,79,175,164,70,78,151,242,123,37,198,48,122,35,112,167,248,228,136,19,121,222,166,134,183, +43,221,213,99,175,255,105,43,15,44,228,15,222,200,20,155,148,63,31,67,16,16,170,210,105,127,32,224,215,127,199,141,226,91,193,128,128,72,241,121,41,126,73,254,124,4,193,161,247,119,75,39,69,57,23,190,249,21,79,180,225,3,226,37,104,66,32,246,197,128,191, +240,43,158,104,199,7,130,77,27,53,102,16,241,132,182,40,148,75,13,181,236,79,84,114,179,63,229,64,130,111,162,28,129,249,252,193,36,132,200,187,87,207,207,252,62,67,149,174,234,124,195,8,56,161,237,66,214,236,66,80,11,184,5,17,130,202,244,197,194,225, +51,3,172,77,247,116,171,143,128,211,73,93,118,22,174,196,71,19,169,141,58,59,119,123,234,140,0,53,224,80,174,44,11,87,33,117,179,158,175,209,74,134,109,7,175,247,214,145,157,157,141,44,35,23,10,54,178,51,163,39,49,103,217,67,174,28,200,132,242,255,32, +141,0,140,101,164,95,108,51,38,44,237,141,246,125,17,67,94,98,84,107,128,176,210,200,80,75,88,187,23,168,233,145,154,87,147,67,69,199,7,104,46,73,39,78,187,151,67,194,158,235,94,120,189,183,238,27,181,122,105,102,212,249,224,245,40,68,177,217,234,48, +211,128,120,2,17,133,73,109,246,147,234,180,59,140,54,228,129,171,4,147,150,53,248,56,68,12,106,110,86,147,195,183,114,147,50,19,59,126,225,147,176,150,14,107,72,77,202,196,26,168,31,160,140,205,206,205,95,160,17,63,181,148,27,223,213,232,245,234,129, +176,64,31,63,33,32,190,12,112,183,129,50,236,126,7,77,32,136,143,16,144,150,59,178,219,61,44,158,35,130,41,127,196,33,120,103,39,191,188,200,230,82,195,75,16,16,207,121,183,176,185,59,144,155,26,117,154,233,86,27,240,135,192,179,161,97,182,57,33,107, +141,62,209,83,46,70,85,60,225,105,128,0,209,43,29,100,28,46,204,97,121,144,104,184,216,130,120,191,171,211,65,81,58,113,195,78,10,85,126,110,34,242,229,107,168,163,80,60,174,174,80,35,38,158,80,78,33,227,183,213,171,114,111,142,120,156,157,142,142,120, +66,57,2,66,84,148,35,192,56,140,121,53,215,15,133,210,0,195,232,72,80,205,45,151,252,78,169,2,148,119,237,161,220,219,146,199,219,200,249,89,24,187,141,65,209,186,71,38,171,216,73,1,108,172,249,11,249,118,144,98,209,11,173,171,211,78,197,216,164,159, +160,209,212,41,222,249,79,208,140,93,45,175,40,219,83,103,195,194,218,4,205,132,178,205,74,107,76,70,185,178,81,223,239,10,49,252,134,178,193,225,116,118,220,90,169,159,224,246,70,148,114,71,203,188,46,236,143,133,221,158,254,5,44,42,182,214,83,216,43, +211,0,0,0,0,73,69,78,68,174,66,96,130,0,0 }; + +const char* moveoff_png = (const char*) temp_binary_data_11; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData13.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData13.cpp new file mode 100644 index 00000000..9eb5e2d7 --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData13.cpp @@ -0,0 +1,30 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== move-on.png ================== +static const unsigned char temp_binary_data_12[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,48,0,0,0,48,8,6,0,0,0,87,2,249,135,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,3,83,73,68,65,84,120, +1,213,153,77,82,27,49,16,133,159,6,3,89,194,146,240,83,195,13,194,13,240,13,224,6,225,4,64,85,128,98,21,103,149,50,80,133,57,65,224,4,225,6,230,6,57,2,19,72,32,187,152,77,138,196,246,116,90,194,118,198,182,100,51,74,11,134,111,101,207,140,60,175,173, +238,150,186,5,4,132,110,15,223,211,77,117,11,1,81,8,4,139,95,69,74,117,243,37,85,101,181,184,115,129,0,4,49,128,110,143,99,80,179,14,66,220,185,212,64,52,185,162,230,182,19,8,19,65,24,139,120,205,12,210,102,157,46,143,103,32,140,184,1,160,214,167,1,241, +93,98,76,183,62,67,24,81,23,210,65,203,126,95,25,249,144,82,53,245,122,103,123,228,239,232,153,122,117,207,179,85,138,209,166,216,12,91,216,61,181,254,28,2,65,223,15,168,239,69,243,187,35,223,69,87,71,107,40,165,199,32,197,194,105,208,213,26,60,126,214, +54,78,222,133,60,81,75,239,206,249,223,62,179,136,215,119,19,215,184,194,24,96,104,77,213,88,108,99,232,186,66,195,53,164,132,130,96,252,126,170,169,215,141,225,25,32,186,115,141,43,196,12,100,196,191,177,63,128,196,53,246,217,13,24,33,62,65,138,19,243, +73,209,211,187,208,184,172,163,25,41,62,106,151,213,252,126,66,215,213,6,167,222,196,249,30,60,19,99,197,207,237,39,255,158,173,204,168,229,138,117,22,254,203,128,135,133,11,91,246,212,231,197,144,248,113,120,27,64,55,135,188,232,144,228,86,57,183,120, +141,127,16,19,222,66,18,15,241,102,24,188,17,115,27,131,143,120,141,88,22,122,76,214,233,66,183,31,99,164,19,151,16,192,58,3,198,191,3,226,243,111,155,132,97,161,207,0,157,218,88,124,93,56,56,101,224,109,186,214,102,10,166,12,61,3,204,141,233,230,23, +22,191,138,162,162,181,233,202,46,99,132,137,129,135,2,220,84,75,189,192,28,220,207,119,201,227,235,94,26,29,239,205,16,27,35,174,15,55,116,163,32,162,31,71,155,15,221,3,217,172,18,152,24,17,213,117,92,68,104,182,103,241,82,105,167,119,145,90,220,171, +232,190,13,175,201,9,94,12,92,244,232,94,211,194,94,205,196,128,246,37,206,205,101,96,162,215,14,9,237,235,78,105,142,247,102,98,131,183,28,45,179,83,213,95,122,89,200,228,102,213,230,153,80,23,40,42,90,219,159,95,43,217,117,164,111,37,238,220,40,243, +30,188,130,162,193,197,13,123,202,208,250,228,191,27,29,76,119,209,228,114,158,214,97,222,182,139,11,175,65,157,98,228,39,4,241,53,32,247,110,52,83,73,73,210,128,39,185,12,24,219,61,240,37,197,25,60,121,180,1,97,196,235,124,78,31,212,226,174,247,230, +209,233,119,116,85,93,83,75,123,231,230,115,142,2,252,169,113,27,112,115,112,201,121,247,20,247,165,147,162,138,215,184,13,200,174,124,24,234,247,143,21,47,149,38,199,97,175,200,250,79,82,226,129,219,133,248,231,187,216,131,216,28,46,88,41,148,120,141, +35,11,241,201,136,13,165,206,139,36,94,99,55,160,169,236,51,192,181,178,171,184,126,46,236,109,149,18,159,75,17,231,104,211,21,230,198,42,17,119,138,211,59,68,124,173,141,175,58,70,212,242,182,247,234,41,137,236,33,223,245,65,141,231,116,115,228,67,102, +225,226,34,74,8,241,212,214,105,203,172,90,111,242,126,158,79,40,203,16,68,254,128,227,119,105,221,81,158,114,193,212,218,128,48,226,6,152,216,208,149,93,255,97,93,176,244,27,228,136,201,8,77,177,222,187,144,170,141,162,165,223,71,65,223,170,91,161,203, +211,191,181,205,150,96,11,168,116,103,0,0,0,0,73,69,78,68,174,66,96,130,0,0 }; + +const char* moveon_png = (const char*) temp_binary_data_12; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData14.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData14.cpp new file mode 100644 index 00000000..148b7f9c --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData14.cpp @@ -0,0 +1,29 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== search.png ================== +static const unsigned char temp_binary_data_13[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,36,0,0,0,36,8,6,0,0,0,225,0,152,152,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,3,66,73,68,65,84,120, +1,205,87,189,114,211,64,16,222,147,9,147,210,37,165,58,38,52,142,171,76,42,204,19,196,36,48,67,103,249,9,172,60,1,118,69,25,65,69,135,66,197,12,144,132,39,64,233,76,21,49,195,144,86,188,129,41,97,198,94,118,207,146,238,172,72,103,89,86,112,190,153,76, +116,231,211,233,187,111,127,110,87,192,10,112,186,110,19,182,192,134,41,236,210,208,78,127,16,16,130,5,145,255,209,11,97,77,136,50,139,156,103,110,7,17,95,2,74,34,77,195,210,136,118,12,196,150,24,249,31,188,8,234,38,164,17,233,192,234,59,251,85,136,21, +18,234,29,14,78,232,159,123,243,5,65,42,96,0,40,126,201,9,36,197,44,108,97,190,122,17,173,31,249,103,158,15,85,9,177,159,96,3,207,51,170,76,104,227,215,112,31,124,211,137,89,81,242,47,7,5,246,244,121,4,28,190,63,123,51,130,42,132,122,71,131,171,216,87, +230,11,4,92,192,84,244,253,11,111,2,37,225,60,119,119,113,74,135,210,28,159,14,116,76,74,121,203,222,93,32,148,53,83,44,247,16,42,128,148,182,99,165,213,225,44,241,196,255,228,5,80,134,144,115,228,118,201,129,207,235,32,163,145,98,243,127,213,72,69,98, +38,218,38,181,173,228,129,200,156,164,100,40,66,214,37,195,224,15,139,169,120,74,143,9,1,155,190,232,154,222,145,132,88,29,80,246,142,128,194,21,106,2,145,226,72,59,78,198,228,224,3,153,96,77,132,144,220,39,153,32,117,130,170,73,173,144,212,60,236,163, +120,72,105,2,186,96,34,68,246,82,11,106,84,71,7,169,116,170,6,248,184,104,157,37,115,135,122,43,172,91,157,20,13,74,31,49,208,144,249,45,152,105,185,98,22,103,223,91,64,124,241,166,206,93,228,71,108,50,59,29,241,173,125,187,80,225,190,13,133,132,238, +20,152,144,98,141,198,210,226,191,96,145,144,110,190,154,17,251,140,157,140,139,130,135,157,58,72,6,104,8,199,181,113,79,221,105,38,95,181,56,147,130,82,169,185,144,6,106,4,206,180,228,11,6,66,114,1,170,164,37,43,196,154,193,55,63,237,172,146,175,208, +146,100,30,33,146,211,79,103,40,105,213,173,18,90,56,0,85,77,70,166,18,68,18,146,73,75,104,153,116,134,239,76,23,224,42,136,15,183,80,99,153,214,167,121,136,202,4,190,145,211,76,202,117,204,186,164,100,145,70,135,75,191,49,47,107,124,40,67,40,91,38,112, +81,37,73,189,96,251,87,32,195,29,139,133,87,160,66,125,82,230,226,110,232,131,240,122,28,182,119,246,185,138,236,196,83,15,168,104,239,210,220,111,254,13,74,128,85,109,61,218,123,69,7,122,11,124,65,40,108,83,157,61,249,126,253,237,210,244,126,110,27, +228,28,186,67,42,164,178,209,22,201,206,163,65,245,82,166,67,149,166,165,60,67,161,125,64,210,58,176,216,14,77,244,241,178,14,164,176,47,147,146,207,237,111,23,44,137,180,231,220,53,66,136,128,90,239,62,252,165,214,72,59,160,137,84,3,10,16,254,28,71, +237,135,251,167,180,233,159,248,131,89,7,111,106,127,55,136,80,135,209,247,63,123,163,240,199,120,66,230,14,116,87,32,165,59,173,157,61,200,51,95,169,222,158,65,102,116,40,131,30,196,29,170,157,67,34,164,107,232,146,11,177,162,60,147,117,133,60,165,74, +19,90,216,152,125,70,171,103,86,169,50,151,145,170,68,104,93,152,72,53,96,3,48,249,212,70,20,74,144,85,138,136,245,55,162,80,130,28,165,190,192,93,0,71,176,140,98,194,63,250,252,124,184,174,6,177,51,0,0,0,0,73,69,78,68,174,66,96,130,0,0 }; + +const char* search_png = (const char*) temp_binary_data_13; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData15.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData15.cpp new file mode 100644 index 00000000..d9067ede --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData15.cpp @@ -0,0 +1,36 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== speedometer-off.png ================== +static const unsigned char temp_binary_data_14[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,48,0,0,0,48,8,6,0,0,0,87,2,249,135,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,5,32,73,68,65,84,120, +1,237,88,61,108,35,69,20,126,51,206,143,148,20,231,43,174,162,192,161,160,64,72,23,36,104,239,108,167,64,116,41,174,191,92,137,144,146,93,233,226,11,13,113,40,238,242,3,90,39,20,87,38,161,37,18,142,68,131,196,197,14,66,72,252,72,23,10,144,160,193,180, +184,192,20,23,29,73,188,195,247,246,39,30,239,174,189,182,227,203,73,119,251,73,235,157,157,157,121,239,123,51,111,222,123,107,162,4,9,94,110,8,26,34,150,172,74,214,38,123,26,66,175,227,49,13,241,105,77,213,145,34,251,47,73,242,104,213,204,85,105,72, +184,176,1,76,26,196,110,43,18,179,228,144,238,9,13,65,162,12,229,187,23,53,102,96,3,92,226,106,89,17,101,233,98,168,65,206,202,134,57,179,67,3,160,111,3,64,60,3,133,219,49,196,27,184,78,52,37,167,24,63,73,93,118,200,219,17,19,59,82,163,62,32,251,25,92, +176,190,153,181,73,61,142,34,47,4,85,241,107,74,18,83,235,102,254,42,165,196,7,232,172,240,165,82,194,224,62,184,217,91,120,127,135,132,42,7,231,99,81,88,118,165,96,85,102,169,15,140,244,58,16,130,151,161,166,24,253,86,153,107,198,76,137,98,176,97,230, +142,112,227,107,167,176,85,185,69,54,173,147,82,83,218,144,12,100,125,9,93,197,117,51,183,66,61,160,167,29,232,64,190,209,106,138,133,162,85,105,115,143,20,169,122,171,157,170,235,239,238,110,124,61,9,242,55,209,252,17,59,244,21,238,127,183,139,86,69, +87,103,60,98,13,96,183,9,146,23,164,54,79,224,42,154,17,153,99,178,231,244,49,15,230,243,135,41,169,30,166,100,234,225,131,249,27,135,109,243,199,71,223,198,237,154,75,64,214,236,179,211,215,224,66,155,20,54,34,214,157,186,26,192,7,22,67,172,128,224, +149,53,115,198,40,153,185,6,252,217,108,9,146,141,224,124,54,34,72,158,145,178,229,177,223,22,82,236,125,178,248,238,19,68,33,131,101,7,116,109,187,28,6,52,0,73,137,201,103,206,149,97,149,214,205,153,162,255,140,136,177,3,35,114,124,113,155,122,196,170, +145,253,9,59,243,49,95,186,129,44,59,176,19,105,142,120,221,100,117,12,163,139,214,163,57,132,54,125,114,29,91,61,197,171,69,207,16,124,62,196,200,232,239,32,246,138,223,231,45,80,53,106,124,199,29,0,249,246,67,36,196,15,114,116,108,117,113,235,224,38, +61,35,176,236,212,216,248,22,72,253,170,247,115,194,236,52,39,210,0,206,178,164,185,14,200,255,137,95,94,249,107,194,22,239,127,184,245,237,27,52,100,176,76,150,173,148,154,84,66,252,131,29,248,195,127,199,121,199,227,20,66,164,1,152,112,187,125,144, +184,135,67,90,167,75,130,163,75,142,20,244,62,37,218,57,181,198,70,128,179,162,223,198,74,236,227,208,125,113,118,250,116,137,195,98,147,154,159,226,224,253,70,67,6,203,116,194,174,144,159,179,174,181,133,27,251,208,93,61,231,164,84,100,72,13,29,98,183, +36,86,21,255,153,83,127,63,17,102,80,120,225,210,185,108,228,23,172,236,17,162,32,72,11,75,227,18,58,204,161,82,130,235,121,221,46,233,166,254,161,129,51,246,177,83,75,217,25,104,65,64,16,211,202,33,173,2,35,197,29,184,82,25,253,86,59,183,214,174,68, +26,192,194,180,109,105,220,119,235,151,190,225,149,31,134,67,5,149,38,171,71,43,123,76,42,227,17,12,81,118,122,81,20,10,37,86,252,149,46,88,7,156,32,211,238,59,121,61,56,62,100,128,32,249,42,121,162,97,200,19,167,232,114,12,83,245,13,100,86,234,141,188, +229,147,247,230,206,197,84,238,248,192,81,187,208,93,94,53,114,85,206,65,5,235,81,49,52,74,169,171,20,111,128,74,171,115,197,52,138,133,187,229,246,11,142,211,212,155,17,76,56,30,188,218,74,137,253,9,84,167,69,51,239,148,34,17,9,84,39,119,133,226,12, +232,134,145,254,134,71,115,208,73,27,185,80,253,148,194,169,179,35,157,171,19,167,0,240,209,209,160,150,0,236,42,237,185,130,83,245,168,194,44,26,92,207,136,96,246,220,157,32,97,68,145,214,193,17,15,17,137,15,108,198,163,176,64,93,190,228,34,150,84,253, +171,61,140,174,207,231,246,168,79,112,81,6,87,72,195,21,188,228,211,94,4,198,65,15,219,56,196,11,45,106,234,151,224,216,40,3,16,117,124,197,148,230,248,220,239,119,42,195,45,143,201,160,11,96,209,170,32,108,170,243,213,135,95,212,130,99,100,184,67,182, +133,77,196,142,105,122,78,16,212,204,232,207,65,110,140,208,14,60,69,226,26,211,158,225,139,89,220,202,110,2,178,231,176,10,141,65,255,2,137,195,82,169,250,78,83,218,19,234,191,211,159,185,108,199,121,204,234,193,55,170,164,142,12,206,240,187,199,228, +173,188,112,82,186,218,244,202,235,12,247,117,171,207,7,5,87,163,77,187,249,145,247,88,87,82,237,137,38,149,206,121,32,122,173,25,249,92,112,94,167,98,238,176,213,166,105,47,46,103,232,242,192,101,251,93,210,221,55,226,0,51,34,13,64,44,46,83,52,26,94, +113,87,165,33,195,175,70,253,178,93,40,245,186,254,94,116,224,212,49,191,223,179,14,42,218,31,88,156,27,54,39,72,150,138,102,247,56,126,81,240,39,165,28,31,123,15,135,111,25,171,254,166,71,178,182,102,230,167,162,198,119,76,173,194,253,7,109,155,183, +14,214,151,86,205,124,141,46,1,222,55,247,94,241,179,239,190,63,62,59,185,15,14,87,132,146,38,37,72,144,32,65,130,4,9,18,36,120,241,240,63,154,84,13,228,104,143,129,176,0,0,0,0,73,69,78,68,174,66,96,130,0,0 }; + +const char* speedometeroff_png = (const char*) temp_binary_data_14; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData16.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData16.cpp new file mode 100644 index 00000000..28ce31a7 --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData16.cpp @@ -0,0 +1,36 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== speedometer-on.png ================== +static const unsigned char temp_binary_data_15[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,48,0,0,0,48,8,6,0,0,0,87,2,249,135,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,4,240,73,68,65,84,120, +1,237,88,77,108,27,85,16,158,89,59,169,68,15,77,15,84,77,66,133,205,129,3,66,106,144,224,220,244,200,45,135,222,147,28,17,135,196,82,227,150,11,117,46,64,236,160,36,92,56,38,225,74,164,58,18,247,132,35,8,169,201,1,36,184,212,148,54,166,24,9,115,48,168, +177,247,13,223,123,182,247,199,222,117,214,63,9,135,238,39,173,237,125,59,59,111,102,222,155,153,239,153,40,70,140,151,27,76,35,132,148,11,179,36,52,67,34,55,161,121,2,234,39,156,135,138,142,200,162,95,49,126,196,147,43,135,52,34,12,237,128,49,90,201, +60,84,205,225,110,34,218,91,92,197,85,36,75,118,135,117,102,96,7,90,209,126,128,104,207,210,112,40,65,207,42,191,150,221,161,1,208,183,3,82,222,72,145,52,182,123,27,142,8,51,157,194,176,54,234,184,46,247,92,33,166,34,241,88,134,39,51,37,234,3,86,63,194, +242,100,109,142,84,227,81,160,241,204,135,184,50,100,217,105,158,94,185,138,223,31,226,141,3,115,89,188,108,198,132,222,193,181,104,140,237,82,78,208,93,63,48,115,244,129,100,84,65,108,153,7,216,235,57,242,132,213,53,94,50,60,149,221,60,75,7,182,201, +17,190,244,181,35,229,245,59,8,68,30,87,218,35,146,162,4,63,196,92,57,228,198,42,69,64,164,21,112,141,247,153,83,117,5,120,73,30,111,116,108,15,85,9,254,13,241,223,11,151,161,239,22,140,255,30,183,223,96,181,254,240,191,42,57,51,103,4,156,233,64,115, +219,116,24,175,104,139,78,147,105,143,19,41,186,116,186,224,21,225,201,236,183,208,254,165,190,204,111,159,82,121,23,31,175,182,36,75,100,61,127,195,232,236,116,34,194,118,234,233,128,73,216,36,111,116,40,94,229,27,217,101,78,103,170,48,36,227,142,123, +86,196,227,68,151,241,26,117,235,31,215,2,217,227,235,235,53,173,83,235,246,201,37,120,219,216,208,3,61,171,144,156,228,31,154,228,114,140,164,45,51,145,87,230,55,148,83,173,232,70,127,245,28,57,240,150,121,111,242,238,79,126,125,249,77,132,117,201,181, +144,15,121,106,229,118,152,158,80,7,228,105,126,1,79,183,221,1,169,80,210,74,243,245,149,26,157,35,76,126,52,232,103,20,134,105,103,80,241,237,176,0,133,111,33,139,252,73,196,252,29,217,244,153,148,243,183,232,156,96,116,219,252,5,140,255,209,247,32, +65,161,9,109,5,43,50,93,54,229,12,48,63,198,103,205,36,158,162,15,218,203,63,74,24,157,208,141,112,163,225,209,95,152,244,23,247,161,204,182,183,106,39,130,87,64,241,188,255,94,238,65,97,133,46,12,102,174,172,111,200,146,249,32,201,144,45,36,222,242, +181,143,6,244,53,253,73,247,155,101,209,250,188,51,241,70,1,163,83,235,87,244,21,37,158,223,71,231,222,55,221,221,69,96,73,237,74,226,22,187,60,112,7,104,113,80,162,213,15,164,252,105,10,196,32,69,182,164,16,109,112,169,241,35,112,174,57,108,31,183,140, +7,36,115,55,149,208,124,222,139,113,211,250,71,103,168,238,216,151,78,103,177,248,41,220,233,130,48,99,242,77,53,39,55,33,21,124,168,250,34,37,236,34,73,194,117,32,161,180,109,135,212,211,1,133,8,184,235,82,229,107,217,129,28,104,210,15,106,245,12,112, +127,109,33,19,138,67,61,101,12,12,226,84,70,20,219,198,6,189,110,69,90,158,21,170,14,139,21,190,217,41,222,237,0,211,235,158,187,154,33,93,6,170,18,216,85,131,140,63,41,108,32,16,158,134,39,11,205,175,176,55,208,197,149,189,139,16,23,181,225,186,7,201, +179,181,92,128,224,85,58,219,1,28,3,165,61,19,143,145,82,119,28,51,202,121,138,228,132,208,2,69,65,51,73,247,233,69,109,135,211,57,67,69,220,6,202,142,50,143,252,21,58,211,129,243,70,128,209,195,32,32,137,197,163,84,49,89,137,189,214,239,200,91,8,178, +96,150,236,239,158,194,187,84,175,45,159,101,180,174,120,88,5,125,198,72,53,71,172,165,94,39,185,32,7,254,70,148,218,234,198,80,159,247,168,79,240,244,189,28,72,217,4,78,98,205,230,163,148,38,129,185,200,239,123,202,54,146,216,37,118,74,142,59,101,187, +29,176,44,212,95,167,235,77,232,250,204,147,31,149,168,79,180,88,235,50,13,1,172,196,140,47,250,150,148,58,101,186,59,177,221,81,247,101,108,134,254,47,40,221,43,60,176,173,174,146,222,189,2,141,228,17,141,215,61,74,212,44,62,139,205,6,212,88,192,50, +86,207,171,51,203,147,245,247,104,76,189,130,45,252,131,161,237,150,153,219,65,16,165,14,60,15,200,211,194,35,36,81,43,242,172,183,212,150,161,215,109,134,218,131,159,15,108,188,97,163,234,227,214,156,21,125,82,3,181,222,116,236,8,57,216,132,145,57,79, +181,129,2,93,151,189,244,250,220,97,104,251,93,55,136,20,152,192,26,193,14,8,23,3,199,117,199,212,228,110,196,209,55,154,219,108,212,161,237,252,38,69,176,41,252,72,121,82,56,112,255,192,210,173,30,181,189,241,239,230,40,154,79,47,200,49,142,148,215, +248,125,204,173,255,182,124,187,53,92,226,233,108,58,72,62,220,1,77,111,37,185,77,182,125,76,73,217,28,164,148,14,3,169,172,77,209,11,250,4,189,228,10,177,157,185,232,249,99,196,136,17,35,70,140,24,49,98,92,0,254,3,217,93,44,68,3,104,87,149,0,0,0,0,73, +69,78,68,174,66,96,130,0,0 }; + +const char* speedometeron_png = (const char*) temp_binary_data_15; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData17.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData17.cpp new file mode 100644 index 00000000..968274d8 --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData17.cpp @@ -0,0 +1,24 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== tab-off.png ================== +static const unsigned char temp_binary_data_16[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,48,0,0,0,48,8,6,0,0,0,87,2,249,135,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,1,168,73,68,65,84,120, +1,237,216,63,75,195,64,24,6,240,231,174,197,185,56,232,170,238,130,126,131,254,25,220,138,171,163,159,160,77,161,70,113,150,182,86,72,179,11,142,93,107,247,54,245,19,40,184,171,223,192,44,157,66,243,122,87,84,74,208,33,228,188,244,240,126,67,201,53,16, +222,135,188,119,92,14,176,44,203,178,44,43,63,76,254,156,123,193,14,129,238,8,56,16,195,18,214,28,3,141,24,184,211,117,42,111,76,22,31,131,30,97,64,225,9,33,7,59,228,49,224,193,188,226,165,146,236,26,14,208,49,12,37,91,190,152,252,243,218,169,50,172, +177,51,111,74,43,195,18,135,225,108,128,188,21,161,193,197,96,90,143,162,104,114,211,62,154,39,239,185,94,48,94,29,247,156,74,29,41,104,121,3,49,43,236,22,138,27,87,248,3,58,91,104,207,245,131,6,20,211,59,7,98,212,84,135,72,61,7,220,193,44,125,43,80, +188,247,125,45,66,180,253,96,222,111,84,110,161,64,250,73,76,180,143,140,196,254,165,222,246,102,243,190,83,30,34,35,45,171,208,79,56,232,68,132,192,114,67,144,233,57,57,42,96,129,172,114,11,192,16,15,187,78,77,127,11,113,198,158,145,18,49,108,81,76, +219,95,99,81,252,88,69,241,82,234,0,157,102,249,18,41,185,254,131,88,58,63,3,16,38,221,86,77,201,10,36,233,109,33,81,124,175,85,241,161,144,190,85,136,227,165,215,80,91,188,164,37,0,167,197,107,180,25,41,107,155,85,90,2,116,154,213,241,111,247,210,238, +62,147,236,7,77,222,140,15,192,196,87,254,59,204,60,23,90,226,226,12,229,9,134,18,181,223,139,0,236,84,92,135,48,79,40,106,111,114,121,64,42,207,24,193,104,4,51,132,140,97,38,107,150,181,195,178,44,203,250,215,62,0,55,170,120,209,82,24,187,200,0,0,0, +0,73,69,78,68,174,66,96,130,0,0 }; + +const char* taboff_png = (const char*) temp_binary_data_16; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData18.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData18.cpp new file mode 100644 index 00000000..270649e5 --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData18.cpp @@ -0,0 +1,23 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== tab-on.png ================== +static const unsigned char temp_binary_data_17[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,48,0,0,0,48,8,6,0,0,0,87,2,249,135,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,1,140,73,68,65,84,120, +1,237,216,79,78,194,64,20,6,240,239,53,192,154,45,10,9,222,160,220,64,111,224,17,240,6,237,162,18,119,176,84,76,208,19,128,55,208,19,232,13,228,8,44,4,150,118,229,66,106,159,111,192,16,241,79,226,164,117,202,132,249,45,128,161,155,247,165,111,218,153, +1,28,199,113,28,199,41,14,169,15,158,15,154,224,100,8,134,47,163,42,182,29,225,22,84,14,169,22,78,104,89,124,154,60,90,81,248,166,24,94,185,85,66,186,24,200,192,182,226,149,170,234,26,79,126,28,195,86,204,126,233,235,127,180,127,74,216,98,60,189,224, +79,195,170,7,203,185,0,69,43,193,0,126,58,15,176,168,140,232,32,140,191,93,219,236,105,237,57,104,234,14,248,168,44,238,241,15,76,182,144,207,211,203,33,114,102,120,14,164,237,188,67,104,207,1,158,245,245,91,97,181,198,250,24,72,136,89,63,166,189,40, +68,14,244,39,49,243,33,178,98,14,120,46,33,106,81,15,25,25,121,10,253,40,229,174,132,80,223,200,162,216,247,64,146,102,94,182,20,23,32,229,30,53,58,93,100,164,223,66,68,15,208,198,178,97,66,115,61,76,113,157,71,241,138,118,0,121,122,28,65,147,188,137, +71,18,188,185,26,208,13,53,162,0,57,49,219,66,170,248,122,212,70,142,76,6,24,231,93,188,98,42,192,24,175,47,218,173,247,23,70,222,3,84,239,92,253,122,45,227,14,208,109,104,138,102,125,0,226,105,255,217,194,67,173,53,79,142,233,198,176,215,157,4,72,78, +228,70,196,176,142,212,236,189,5,30,213,206,38,240,146,214,242,192,212,10,82,184,90,143,73,205,203,218,29,199,113,156,157,246,14,130,136,121,153,169,217,8,58,0,0,0,0,73,69,78,68,174,66,96,130,0,0 }; + +const char* tabon_png = (const char*) temp_binary_data_17; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData19.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData19.cpp new file mode 100644 index 00000000..f032c2ff --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData19.cpp @@ -0,0 +1,28 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== timing-off.png ================== +static const unsigned char temp_binary_data_18[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,27,0,0,0,32,8,6,0,0,0,227,105,3,59,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,2,231,73,68,65,84,120, +1,221,87,221,109,19,65,16,254,102,29,243,138,169,128,165,3,167,131,164,130,196,9,72,60,37,151,6,146,75,5,113,58,56,211,0,23,158,144,32,137,59,136,169,0,211,193,150,224,71,20,97,15,51,123,235,99,157,248,236,59,99,241,192,39,89,218,211,206,206,183,51,59, +127,38,172,64,210,75,115,38,62,69,77,208,11,122,147,127,206,92,213,190,193,63,196,255,75,182,179,114,215,96,72,76,14,117,241,19,147,85,219,132,154,72,222,167,22,143,216,147,101,39,252,156,92,198,225,23,198,249,48,155,212,209,177,146,44,57,76,45,27,92, +0,156,4,130,42,45,35,241,192,77,126,151,229,104,74,38,36,29,54,124,37,203,20,205,224,136,232,50,191,205,134,203,54,159,5,72,242,46,237,10,209,247,152,72,20,140,8,116,73,45,218,165,25,189,186,185,27,144,230,20,25,218,87,139,148,36,136,90,102,190,63,57, +58,191,194,58,203,60,209,148,31,16,92,166,36,104,227,108,85,162,150,103,143,210,148,225,189,209,9,154,243,155,219,193,217,82,178,226,125,188,69,5,145,88,34,111,144,161,1,130,14,189,172,213,111,33,239,127,186,251,112,61,223,47,221,24,132,54,38,82,72,84, +58,113,243,62,130,91,69,79,63,121,155,238,45,144,137,11,146,249,109,68,224,122,19,162,37,132,62,29,120,198,31,23,200,130,175,21,78,136,250,104,0,13,6,159,131,79,9,65,131,240,105,147,227,244,208,147,133,133,23,86,171,208,0,39,199,231,23,234,42,126,244, +79,176,136,25,212,59,133,117,224,11,79,198,140,131,72,96,136,154,208,96,144,176,207,194,37,7,79,247,181,170,132,180,80,182,61,205,93,3,226,174,63,32,97,94,183,236,68,81,39,199,105,80,249,198,6,163,114,189,131,174,17,86,139,194,170,31,168,9,110,249,71, +183,98,210,56,191,207,170,171,204,20,227,114,61,131,213,0,153,39,97,61,171,52,148,217,23,100,71,109,234,173,17,143,117,218,198,253,44,255,154,141,196,229,61,41,93,189,58,149,37,38,214,126,166,7,196,37,252,186,238,169,170,66,251,12,109,209,59,13,107,130, +147,0,33,239,87,46,92,179,93,76,209,141,214,206,16,227,91,248,180,113,105,217,6,162,201,204,73,164,143,141,68,73,94,110,114,253,177,109,29,52,61,66,32,137,243,138,20,48,62,183,194,135,108,38,218,102,176,5,72,135,255,211,211,218,69,101,242,209,40,77,240, +178,20,154,242,189,102,59,254,2,69,243,244,163,132,90,149,207,163,214,147,229,95,178,177,182,149,32,107,37,105,31,54,37,148,122,121,170,245,50,124,186,185,85,158,120,65,80,198,109,138,30,85,90,255,126,221,92,90,50,183,76,228,252,110,124,158,158,30,152, +17,50,138,231,123,113,131,84,138,235,42,82,239,1,131,36,84,118,91,18,181,228,162,226,177,88,118,249,116,117,148,246,163,30,55,151,212,131,78,10,175,175,161,12,188,244,69,156,125,46,117,98,57,45,99,203,46,87,57,55,250,208,37,244,27,252,139,153,172,235, +242,107,39,98,79,106,112,40,86,28,72,150,90,177,216,150,202,73,70,243,153,20,133,22,134,90,51,215,233,250,13,140,194,63,59,254,43,233,232,0,0,0,0,73,69,78,68,174,66,96,130,0,0 }; + +const char* timingoff_png = (const char*) temp_binary_data_18; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData2.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData2.cpp new file mode 100644 index 00000000..bf3c71db --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData2.cpp @@ -0,0 +1,36 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== clear.png ================== +static const unsigned char temp_binary_data_1[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,54,0,0,0,54,8,6,0,0,0,140,69,106,221,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,5,32,73,68,65,84, +120,1,213,154,191,83,27,71,20,199,191,123,39,35,108,96,116,144,9,200,54,16,217,51,100,66,6,140,82,89,157,69,231,46,249,15,34,255,5,56,101,42,226,46,165,243,23,216,116,41,113,151,206,114,101,117,145,131,146,204,56,147,68,19,203,65,34,193,136,12,216,144, +65,218,188,183,210,41,112,250,117,187,58,9,241,105,164,59,238,142,251,232,189,219,219,125,187,2,1,19,143,38,146,85,41,227,82,138,21,33,68,28,144,142,4,98,167,143,17,64,158,246,229,233,91,89,160,250,204,18,34,155,45,102,210,8,16,129,0,96,153,74,21,159, +67,200,207,232,146,14,140,144,101,72,177,105,91,216,8,66,210,88,44,30,75,58,213,183,71,107,82,200,251,230,50,109,201,211,157,61,216,42,102,30,195,16,35,177,91,51,183,215,40,149,190,234,131,144,23,99,65,45,49,74,185,88,69,226,17,125,77,98,176,228,109, +129,85,74,209,188,223,19,108,191,7,114,148,170,192,183,244,91,124,132,193,67,13,144,72,69,199,231,142,75,135,133,140,159,19,124,137,221,154,78,172,75,33,190,38,169,81,156,31,252,191,239,70,199,102,65,114,207,186,29,220,85,108,101,58,241,72,10,220,199, +176,32,144,156,25,155,141,237,28,22,158,116,58,172,163,24,75,85,5,82,24,54,4,226,221,228,218,138,213,210,111,136,34,229,133,228,58,165,101,75,49,213,156,171,103,106,200,161,180,164,6,101,191,85,131,210,212,220,215,154,116,249,253,0,222,81,1,33,202,182, +144,159,120,95,5,150,247,48,122,79,61,189,56,82,140,116,234,239,214,51,156,73,197,229,104,34,69,31,41,92,60,98,222,148,60,27,49,137,117,24,16,14,135,113,109,254,58,122,225,202,216,21,44,198,151,17,30,13,195,4,234,226,173,115,255,213,221,110,68,204,52, +90,44,181,176,188,136,200,212,148,186,169,242,155,61,232,194,82,11,75,139,24,161,243,157,247,38,177,79,215,168,156,84,160,201,40,157,116,92,58,40,164,121,227,255,136,25,68,203,149,26,9,215,126,229,169,233,247,17,91,184,169,115,137,134,148,29,10,169,109, +190,22,111,155,68,142,198,128,107,238,119,37,198,227,41,120,6,131,221,240,74,185,232,200,121,165,92,204,229,164,179,84,115,169,137,169,65,162,38,179,55,63,104,146,114,241,35,215,78,202,197,182,109,88,182,239,62,122,3,122,127,41,151,90,42,170,145,175, +30,249,95,126,197,187,195,183,109,255,222,73,174,155,84,229,228,4,47,115,63,119,188,126,91,164,80,46,66,13,235,213,187,75,31,59,100,227,195,165,143,113,153,110,180,29,111,118,254,162,31,225,183,198,118,95,165,234,80,87,112,213,226,194,11,12,225,150,235, +101,238,39,223,145,27,132,20,99,73,17,15,73,88,119,212,91,192,16,87,174,83,228,88,206,182,67,24,143,76,244,93,138,161,58,204,138,197,45,9,122,196,79,228,34,244,126,26,132,20,35,40,11,185,241,136,33,0,252,200,181,62,47,88,41,134,202,8,78,96,98,140,174, +92,63,164,234,196,44,4,140,142,92,159,164,20,129,139,49,220,43,25,9,143,116,61,110,230,90,20,253,34,112,177,110,77,250,105,76,250,150,126,97,177,60,2,66,71,202,165,79,114,121,75,208,208,26,1,96,34,229,18,184,156,32,49,106,244,95,160,71,130,232,81,4,42, +39,197,190,69,131,152,44,122,192,175,212,193,254,63,90,221,175,94,160,30,126,218,170,210,164,27,12,209,141,148,110,223,210,148,170,144,89,85,126,91,158,185,189,167,91,153,234,37,253,76,70,5,254,17,229,173,210,243,73,213,220,211,148,234,19,221,211,199, +35,17,227,103,202,111,228,184,254,161,141,144,155,252,161,196,104,122,232,49,52,217,249,115,27,219,175,94,55,237,247,219,77,234,38,183,253,170,128,242,174,126,97,136,198,41,27,252,217,168,4,155,164,35,115,117,126,22,87,231,174,215,111,86,191,239,215, +42,45,89,106,251,143,215,208,133,39,237,127,40,101,110,168,235,186,59,163,19,115,151,97,48,83,201,173,29,229,178,122,230,76,250,126,178,42,177,247,247,46,34,147,14,46,141,92,50,150,82,8,124,177,115,80,200,214,190,214,137,59,73,167,18,126,247,187,105, +121,155,11,59,255,30,31,195,20,142,156,67,181,201,93,106,52,76,56,29,45,117,61,247,75,241,40,127,20,29,159,231,59,187,11,3,42,21,237,2,231,25,56,114,61,245,244,79,69,171,182,233,97,121,38,193,133,157,36,46,16,222,104,49,77,189,123,154,157,191,167,22, +147,92,16,4,221,43,117,120,87,189,251,155,42,146,197,131,66,185,151,148,28,52,85,88,95,110,149,50,223,121,247,183,44,181,242,116,204,244,196,44,181,117,195,157,146,84,63,124,240,99,41,211,114,230,181,109,13,153,30,196,52,77,96,223,224,185,94,12,35,2, +27,185,98,166,237,28,121,199,226,56,205,202,111,14,165,28,73,109,21,51,169,78,135,116,173,250,179,220,48,165,37,167,95,167,72,185,248,154,206,224,180,228,169,80,186,108,226,188,86,231,112,235,199,13,69,187,103,170,249,120,13,120,69,1,189,71,159,202,0, +107,145,190,16,72,83,4,238,233,44,18,51,90,214,199,211,186,130,102,64,251,45,200,81,170,192,162,150,239,249,67,104,210,211,10,211,126,9,170,180,19,226,155,208,232,232,195,108,62,109,212,89,8,100,233,44,79,143,210,133,72,82,126,42,13,59,209,44,35,133, +216,228,241,84,238,60,151,206,182,131,37,45,94,236,12,220,161,225,140,67,255,32,38,165,103,177,51,149,199,168,121,35,17,170,77,72,249,130,235,46,185,128,23,59,255,7,226,148,132,15,60,166,43,59,0,0,0,0,73,69,78,68,174,66,96,130,0,0 }; + +const char* clear_png = (const char*) temp_binary_data_1; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData20.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData20.cpp new file mode 100644 index 00000000..3f58919f --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData20.cpp @@ -0,0 +1,28 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== timing-on.png ================== +static const unsigned char temp_binary_data_19[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,27,0,0,0,32,8,6,0,0,0,227,105,3,59,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,2,248,73,68,65,84,120, +1,221,87,75,78,27,65,16,173,234,25,67,118,33,155,72,136,79,134,27,12,55,176,79,144,112,2,204,9,192,18,24,101,133,189,138,8,72,192,9,48,39,192,55,176,115,2,38,39,200,40,88,33,75,86,17,193,51,93,169,238,158,143,191,67,27,172,44,120,210,88,221,170,158,121, +221,213,85,175,202,8,5,160,222,113,11,16,183,193,22,34,222,192,229,207,225,84,51,252,71,188,94,50,183,208,42,157,54,56,113,8,182,120,120,115,95,100,70,176,4,221,125,241,32,118,202,32,112,9,72,242,131,33,56,252,60,184,1,110,212,238,109,190,129,197,4,103, +76,16,237,242,170,42,207,150,166,127,5,187,32,233,10,87,235,45,152,149,140,126,156,45,193,98,116,4,68,123,48,27,66,136,169,134,235,135,237,73,198,177,0,161,222,153,15,139,253,155,33,34,181,115,196,26,16,108,194,227,159,119,184,82,71,149,83,32,177,194, +235,174,120,203,97,178,210,99,215,94,211,221,201,17,60,117,50,77,132,81,39,115,153,38,137,118,138,18,53,127,247,120,15,80,28,229,238,22,45,92,217,223,153,72,166,239,71,70,55,217,98,98,119,172,30,158,195,12,208,65,68,78,135,61,224,25,62,108,224,242,65, +51,181,231,110,164,126,231,37,68,122,231,202,3,24,87,50,183,74,106,208,237,73,121,136,140,122,95,171,217,110,36,53,159,67,52,70,8,104,210,65,208,229,16,25,255,166,23,26,226,218,97,3,102,128,10,6,125,5,163,132,32,47,146,169,71,191,78,63,25,222,159,60, +72,79,69,208,132,25,64,191,79,119,149,171,64,170,43,24,193,227,194,121,118,58,160,93,77,6,14,124,204,22,244,75,109,176,37,210,9,47,141,187,137,46,70,237,90,85,84,90,24,123,89,229,174,224,129,111,172,216,181,149,29,77,68,201,105,36,92,76,189,99,41,186, +217,216,141,124,193,145,227,25,3,125,7,91,80,116,153,184,62,192,181,250,116,149,41,245,131,108,140,228,137,60,129,201,238,84,42,148,217,45,160,164,73,196,91,133,139,7,171,128,33,155,13,184,118,208,133,88,108,241,201,182,108,148,37,127,17,238,93,157,128, +218,37,248,193,250,189,245,125,187,64,42,245,189,124,226,132,28,32,144,250,181,12,243,135,159,141,92,201,100,136,223,146,169,55,40,45,115,129,200,58,179,16,223,215,3,1,127,221,214,4,227,139,97,210,67,7,146,66,215,124,94,229,150,42,37,26,178,202,58,233, +195,60,32,227,188,166,137,88,43,147,137,70,73,181,204,128,112,173,43,245,11,96,138,167,172,38,211,86,26,181,154,140,123,135,64,149,149,196,232,193,66,191,243,92,66,186,59,221,214,122,105,16,166,167,210,60,67,11,123,39,220,110,211,118,190,176,84,193,229, +90,8,54,36,99,125,11,139,176,136,54,7,115,17,199,94,40,69,231,3,132,160,202,59,8,167,57,141,52,33,169,106,101,79,171,135,34,34,170,104,143,13,96,114,119,117,123,220,224,208,25,109,90,2,35,0,137,134,146,120,203,151,224,51,129,63,210,230,5,74,198,38,169, +203,212,190,209,52,165,162,97,255,47,70,157,70,22,86,249,39,59,98,211,196,184,170,210,114,221,35,111,200,85,234,94,101,204,162,224,180,181,102,62,129,127,223,116,102,170,183,33,227,63,0,0,0,0,73,69,78,68,174,66,96,130,0,0 }; + +const char* timingon_png = (const char*) temp_binary_data_19; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData3.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData3.cpp new file mode 100644 index 00000000..c011f9e4 --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData3.cpp @@ -0,0 +1,29 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== dogfood-off.png ================== +static const unsigned char temp_binary_data_2[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,48,0,0,0,48,8,6,0,0,0,87,2,249,135,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,3,54,73,68,65,84,120, +1,237,151,191,83,19,65,20,199,191,111,131,76,6,11,175,164,51,206,232,56,99,99,202,116,38,99,131,29,157,90,169,127,128,3,97,8,67,103,104,84,108,46,113,198,97,172,132,142,82,58,71,208,208,218,133,134,193,177,48,118,52,206,132,2,6,209,236,243,237,133,96, +46,183,200,5,50,73,138,253,52,119,251,246,199,125,223,238,123,187,123,128,195,225,112,56,28,14,135,195,225,112,56,6,3,161,7,204,188,94,207,140,176,74,67,243,174,66,162,250,50,159,171,25,251,180,95,241,70,149,126,0,141,113,173,120,71,233,196,77,128,167, +9,244,126,49,159,123,130,30,160,112,65,138,239,42,201,145,134,154,48,34,101,62,210,26,250,113,161,188,49,97,234,70,229,189,105,151,15,105,181,36,226,139,242,234,49,120,178,40,206,161,7,140,224,130,252,250,137,20,18,244,72,94,61,16,111,130,177,69,154, +50,179,229,13,79,196,183,139,172,7,109,228,169,152,30,22,103,114,117,244,128,115,133,80,16,50,13,149,145,245,59,100,141,67,34,90,18,225,201,227,17,63,128,249,139,229,75,73,102,242,8,188,43,165,250,24,212,114,49,127,113,39,186,94,129,130,95,73,83,67,7, +33,34,51,28,204,0,51,86,228,121,223,136,36,230,175,108,235,200,226,104,83,188,193,219,135,158,148,231,178,201,147,75,242,78,10,201,49,173,86,187,117,170,107,7,8,58,27,181,5,194,202,34,18,108,213,142,203,178,74,99,204,124,32,142,238,55,251,32,85,120,83, +25,167,35,73,114,19,90,218,84,52,157,66,87,122,186,96,214,95,207,42,168,172,204,248,13,17,116,75,76,151,216,196,54,243,158,140,180,221,18,215,94,223,49,196,111,169,93,11,218,72,248,41,86,87,228,45,35,73,93,55,185,99,236,137,17,181,250,226,105,110,7,49, +137,189,11,21,252,143,105,35,62,232,68,116,189,37,78,68,123,34,246,170,108,141,247,168,153,164,48,101,68,197,35,100,211,148,146,21,153,23,103,179,96,154,148,62,119,140,249,143,214,93,237,80,177,28,48,113,10,149,152,56,49,16,126,116,182,33,89,1,110,238, +52,166,126,15,103,96,194,78,218,175,73,191,86,94,164,2,187,70,242,64,5,97,21,139,88,14,200,180,165,204,192,173,178,204,220,182,40,254,134,118,241,198,246,143,90,156,113,197,137,170,244,123,43,35,46,200,115,229,164,66,206,142,184,171,240,223,28,152,243, +43,207,204,201,9,160,39,135,206,57,168,137,196,229,87,249,220,194,105,13,78,117,64,196,251,199,226,7,142,228,87,73,174,30,121,123,157,133,121,191,146,210,224,239,24,34,20,232,90,235,142,21,182,91,208,104,164,49,100,52,44,231,143,193,122,144,49,40,219, +190,52,146,108,229,197,252,221,190,134,83,193,255,84,146,208,153,106,149,101,166,173,147,170,236,70,186,29,46,171,77,244,159,106,184,24,214,212,194,234,0,119,120,75,49,183,197,94,146,232,152,52,142,187,2,230,178,134,240,182,89,127,158,207,85,209,103, +142,19,182,253,98,231,153,205,165,179,93,196,1,66,35,212,72,14,169,190,139,63,249,118,71,24,217,18,57,226,128,73,224,176,129,183,48,32,100,43,15,125,219,150,200,145,93,72,50,63,116,234,138,67,83,115,254,231,41,12,3,100,110,175,97,44,73,204,53,12,43,172, +35,151,196,136,3,71,80,165,65,236,58,103,97,52,145,104,235,180,71,28,40,201,47,157,132,81,78,146,119,19,67,130,209,98,52,217,174,18,14,135,195,49,88,254,2,12,202,28,41,19,177,104,245,0,0,0,0,73,69,78,68,174,66,96,130,0,0 }; + +const char* dogfoodoff_png = (const char*) temp_binary_data_2; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData4.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData4.cpp new file mode 100644 index 00000000..8edd82d9 --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData4.cpp @@ -0,0 +1,29 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== dogfood-on.png ================== +static const unsigned char temp_binary_data_3[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,48,0,0,0,48,8,6,0,0,0,87,2,249,135,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,3,19,73,68,65,84,120, +1,237,150,191,79,20,65,20,199,191,111,128,243,80,19,206,198,0,154,112,36,18,19,27,41,233,60,58,236,232,212,202,248,23,0,9,16,172,212,74,129,226,142,202,22,59,74,233,40,175,212,238,108,8,86,156,9,191,98,227,82,156,18,46,183,207,183,179,123,176,191,144, +61,239,114,183,197,124,18,184,217,183,111,102,190,111,230,189,217,1,12,6,131,193,96,48,24,12,6,131,193,208,27,8,29,128,15,86,167,160,48,41,205,19,80,166,66,35,11,85,109,255,85,204,161,118,254,92,102,25,6,212,158,248,60,132,141,121,153,246,51,221,91,124, +133,14,208,118,0,188,255,54,139,204,224,74,112,84,250,66,163,203,59,124,180,58,15,70,206,51,206,201,191,102,219,194,121,255,56,141,47,88,104,147,126,180,203,141,219,121,192,126,169,133,18,202,96,254,38,127,83,124,248,33,119,41,30,206,82,89,222,179,5, +226,23,157,16,239,14,251,31,184,41,163,166,68,248,25,108,58,19,203,71,89,245,172,59,34,237,128,237,175,209,94,42,43,129,73,144,124,162,131,25,204,108,210,157,30,236,0,31,172,77,138,136,25,17,233,26,136,197,72,159,164,245,76,26,89,201,241,239,241,203, +34,193,146,212,136,30,68,118,162,86,159,149,214,166,91,39,210,86,210,119,48,179,213,106,80,173,167,144,226,130,8,8,226,172,42,176,225,181,163,125,108,186,37,59,115,83,222,253,22,245,53,207,47,207,63,215,134,241,71,23,121,78,143,233,5,133,22,104,41,133, +248,120,189,0,219,46,72,183,9,153,240,145,244,30,208,5,201,124,42,237,221,11,113,129,247,254,1,80,23,225,219,174,139,147,122,106,72,140,146,138,108,233,218,209,246,190,45,26,93,220,67,66,84,82,71,157,58,90,188,22,242,224,82,156,147,215,24,147,198,83, +89,233,102,209,142,69,196,107,113,62,155,141,188,164,225,138,8,119,118,116,22,164,158,120,47,102,117,90,117,50,0,61,160,194,140,207,244,3,81,113,187,146,94,150,39,238,20,215,225,164,157,141,237,139,186,0,231,221,31,169,5,39,173,18,146,172,6,106,245,188, +76,152,245,77,190,43,41,48,32,43,55,129,166,120,22,91,19,69,85,81,50,118,237,184,138,43,50,70,69,183,253,165,195,24,230,253,98,46,201,81,251,207,26,144,156,127,227,126,57,57,241,150,118,152,170,44,198,38,141,44,189,187,202,225,202,0,248,104,189,40,171, +58,143,52,64,84,162,209,165,133,216,87,113,70,62,46,230,97,215,247,145,38,84,99,156,70,94,87,35,230,88,231,122,99,18,105,163,209,87,136,51,199,23,177,178,131,206,140,13,186,191,220,213,116,146,99,187,36,249,49,231,51,197,46,106,252,14,40,122,28,124,86, +101,116,159,74,224,41,172,169,105,142,237,202,161,104,251,237,42,186,77,95,163,28,120,102,78,182,3,250,139,27,60,54,45,186,187,92,65,151,113,11,150,252,223,129,28,31,191,207,135,253,162,59,96,171,160,19,81,215,197,95,206,29,74,163,152,66,142,6,16,46, +96,219,187,100,245,130,232,220,145,52,138,57,133,156,244,241,125,30,228,36,224,195,181,57,164,1,194,80,216,164,98,156,170,72,43,28,189,36,70,3,56,207,148,82,26,68,85,78,166,82,216,24,9,64,223,0,169,49,45,197,91,70,90,112,180,168,198,116,220,85,194,96, +48,24,122,203,95,179,89,26,222,104,18,232,51,0,0,0,0,73,69,78,68,174,66,96,130,0,0 }; + +const char* dogfoodon_png = (const char*) temp_binary_data_3; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData5.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData5.cpp new file mode 100644 index 00000000..30ec5db7 --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData5.cpp @@ -0,0 +1,31 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== enabled-off.png ================== +static const unsigned char temp_binary_data_4[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,48,0,0,0,48,8,6,0,0,0,87,2,249,135,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,3,156,73,68,65,84,120, +1,237,89,93,82,219,48,16,222,53,237,12,240,196,17,220,19,52,61,65,19,56,0,112,2,194,9,136,31,160,157,190,36,60,181,192,12,78,79,64,114,130,166,7,40,78,79,80,122,2,210,27,240,4,83,40,222,238,38,14,99,201,88,150,113,148,78,135,124,51,144,72,145,246,71, +187,90,237,74,0,11,44,240,188,129,224,8,7,167,209,14,32,117,249,235,213,146,183,212,249,184,247,182,15,14,224,129,43,32,117,248,255,26,255,249,247,241,125,23,28,193,157,2,44,120,234,251,26,56,130,75,5,230,130,133,2,255,26,255,189,2,47,160,36,58,97,180, +118,13,113,147,56,60,158,4,27,61,168,136,170,244,74,89,224,125,24,249,215,64,17,199,200,16,1,207,222,133,231,17,84,4,11,127,54,165,119,16,158,95,10,143,50,243,173,21,16,194,241,88,120,168,77,251,120,213,234,80,25,184,149,106,140,121,148,81,194,74,1,49, +115,34,188,175,176,70,26,64,69,112,42,48,212,186,68,137,47,194,211,98,186,157,2,215,0,109,208,132,103,244,87,200,219,133,138,248,13,184,205,182,212,23,162,118,51,225,89,136,194,92,104,63,252,214,20,255,84,103,225,224,184,213,216,54,205,99,127,166,116, +251,56,88,55,242,58,56,61,239,177,52,59,233,62,15,176,241,41,104,12,77,243,10,45,192,194,183,213,54,140,86,9,42,175,188,142,91,196,150,208,78,247,177,43,157,21,205,51,42,32,171,15,186,223,243,170,116,130,198,21,204,24,93,166,25,143,221,73,129,159,200, +144,11,207,252,35,42,38,69,196,30,155,116,4,142,112,18,52,46,248,163,111,146,65,71,174,2,18,202,244,48,137,4,135,224,24,44,112,39,221,22,25,76,97,53,87,129,123,136,235,233,54,34,12,93,174,254,20,194,67,15,173,49,196,91,121,227,115,21,64,240,54,211,109, +34,250,10,115,2,129,202,11,209,123,157,55,214,176,7,200,87,7,122,23,48,39,16,120,67,165,77,84,207,27,107,218,196,126,186,177,12,80,86,129,135,72,165,135,199,34,220,101,199,231,158,202,38,5,148,73,101,67,39,111,198,32,17,156,195,35,149,218,252,221,44, +175,92,5,74,167,211,182,224,205,216,227,143,30,56,134,201,2,202,42,216,38,87,179,64,43,203,43,215,250,214,10,220,102,147,57,103,88,78,165,236,2,211,30,50,41,240,61,221,248,163,157,11,46,193,113,191,166,117,253,202,27,107,10,163,74,212,225,28,104,19,230, +4,157,23,183,115,235,14,47,255,7,79,153,84,116,164,207,10,143,165,48,144,45,122,30,144,171,192,99,71,58,89,22,25,85,160,243,40,74,97,140,217,40,155,174,175,18,167,230,135,48,170,129,35,76,86,159,154,233,62,36,52,94,10,23,86,100,124,243,112,73,106,4,26, +173,2,190,153,117,77,48,185,94,161,31,144,226,37,209,231,40,88,127,101,154,103,83,145,233,213,151,127,3,113,8,51,6,215,221,82,125,249,26,239,194,19,220,234,125,128,171,162,46,19,219,83,39,210,96,5,188,221,170,150,144,149,231,2,62,204,184,14,208,231,163, +96,163,85,52,223,234,86,226,14,188,14,104,201,28,241,125,142,152,188,74,100,218,231,253,36,52,178,194,195,104,101,194,179,16,214,47,52,201,6,139,232,145,19,153,173,211,99,66,135,182,5,79,18,42,219,186,224,19,90,48,194,201,109,196,200,134,86,169,39,38, +147,18,9,177,161,20,35,82,59,72,250,61,117,47,113,19,73,69,228,52,151,67,202,112,163,119,193,89,236,118,153,202,175,244,27,153,36,90,47,33,238,232,123,162,42,196,231,197,109,202,238,169,39,63,242,177,53,248,70,153,218,84,49,201,75,92,102,183,232,2,203, +48,191,26,198,138,32,237,16,149,187,232,149,19,86,14,169,164,110,120,50,102,246,204,154,68,163,122,140,49,39,98,232,115,152,146,246,52,175,191,98,129,217,53,104,72,228,253,228,208,55,152,199,13,199,2,11,60,7,252,5,44,12,88,217,194,225,230,101,0,0,0,0, +73,69,78,68,174,66,96,130,0,0 }; + +const char* enabledoff_png = (const char*) temp_binary_data_4; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData6.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData6.cpp new file mode 100644 index 00000000..a23f5007 --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData6.cpp @@ -0,0 +1,30 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== enabled-on.png ================== +static const unsigned char temp_binary_data_5[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,48,0,0,0,48,8,6,0,0,0,87,2,249,135,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,3,142,73,68,65,84,120, +1,237,89,93,78,27,49,16,30,111,18,82,169,47,188,210,22,105,57,65,211,27,132,19,0,39,32,156,160,32,181,145,250,212,240,84,41,84,10,61,65,194,9,10,39,32,61,1,233,9,216,170,252,188,230,9,149,238,174,167,99,167,64,236,196,94,155,205,166,170,200,247,16,101, +189,182,103,198,51,59,127,6,88,96,129,167,13,6,5,1,175,63,111,3,199,67,250,55,132,32,104,177,149,119,71,80,0,2,40,10,200,91,244,179,76,255,66,224,252,16,10,66,129,2,16,227,15,88,134,130,80,156,0,115,194,66,128,127,141,255,94,128,50,120,2,207,59,203,80, +77,26,228,34,135,236,85,179,7,57,145,119,63,47,13,224,117,39,132,165,248,20,16,59,20,65,186,120,117,112,10,121,81,141,187,247,251,93,182,207,37,13,15,56,11,32,55,198,88,48,92,123,24,196,58,228,5,194,230,216,19,197,140,248,212,71,8,39,1,164,154,5,243, +170,111,23,113,252,24,242,130,177,190,54,34,132,248,42,105,58,192,77,3,213,228,227,4,243,200,142,224,246,102,7,242,226,182,188,53,229,32,106,146,166,3,50,115,33,188,104,55,132,125,106,171,142,217,139,230,150,117,221,101,27,149,37,47,155,204,78,231,160, +7,12,183,149,65,206,214,217,234,251,190,109,93,182,6,2,208,79,34,154,201,201,235,136,203,187,116,48,145,74,27,187,89,203,172,2,200,211,215,77,39,72,215,217,90,107,8,51,6,91,219,27,2,7,93,171,161,228,193,2,187,6,2,182,173,141,244,216,202,135,8,10,2,197, +129,1,121,182,163,12,30,212,215,166,23,35,183,169,185,201,32,221,135,162,81,18,105,248,56,35,88,199,235,79,161,105,186,89,3,105,92,87,158,201,221,21,121,250,247,100,4,13,221,181,98,176,105,154,111,22,128,193,134,58,192,79,96,126,80,105,33,123,109,154, +104,22,0,89,168,60,167,193,0,230,5,142,125,109,164,110,154,106,211,64,168,60,39,101,79,1,216,184,167,138,192,7,113,37,210,70,140,81,217,226,133,80,89,36,221,156,15,16,247,70,126,157,4,65,240,250,248,167,208,50,10,224,157,78,59,51,49,74,141,123,80,48, +44,26,80,76,0,92,147,171,89,96,10,45,163,246,45,223,0,170,139,158,199,33,204,11,207,146,154,58,192,34,211,84,179,0,28,191,41,207,201,12,114,127,87,32,104,2,224,15,211,84,179,0,129,238,54,131,13,152,31,84,90,104,174,59,44,38,148,168,139,100,72,247,43, +247,30,131,169,41,76,41,237,155,230,27,5,152,26,210,121,234,84,100,228,130,78,35,35,133,177,103,163,92,203,12,129,55,40,189,173,65,65,24,105,152,55,192,202,131,138,236,138,236,170,125,174,213,4,17,252,174,188,241,14,108,89,116,100,123,37,62,211,105,81, +37,183,102,91,151,93,145,165,76,175,190,168,181,146,118,96,214,168,166,221,201,186,59,59,130,59,221,15,224,207,246,33,137,250,86,91,121,12,183,149,157,188,154,144,39,47,15,68,55,29,248,194,86,155,187,89,235,221,186,18,73,165,69,28,171,110,85,244,115, +150,226,179,60,158,9,47,58,53,105,54,58,243,194,76,147,155,150,203,30,206,55,52,178,42,194,210,100,111,72,34,232,65,80,218,103,43,123,145,219,94,36,180,244,54,19,140,11,68,178,238,118,44,158,188,174,152,236,66,192,93,147,234,132,118,29,192,175,242,224, +206,188,164,153,136,84,36,97,117,16,65,202,220,209,27,16,243,91,62,149,159,247,29,153,100,166,28,183,38,190,137,188,32,155,23,102,227,219,241,120,244,37,159,108,119,136,158,145,73,27,238,136,168,129,181,147,213,192,50,33,247,45,229,72,16,106,125,248, +54,122,133,185,81,144,202,219,162,159,217,53,171,252,62,210,82,157,180,178,241,183,158,14,31,170,58,170,45,68,122,142,208,39,198,191,139,60,107,30,29,142,5,22,120,10,248,3,143,192,122,241,235,203,106,176,0,0,0,0,73,69,78,68,174,66,96,130,0,0 }; + +const char* enabledon_png = (const char*) temp_binary_data_5; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData7.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData7.cpp new file mode 100644 index 00000000..fd8606bf --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData7.cpp @@ -0,0 +1,20 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== expand-off.png ================== +static const unsigned char temp_binary_data_6[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,244,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,0,194,73,68,65,84, +120,1,237,151,205,13,2,33,16,133,223,24,11,217,86,208,6,140,13,88,130,225,98,44,193,112,217,181,4,27,176,4,183,20,183,146,69,56,144,16,163,146,101,198,184,137,243,157,128,129,188,199,223,4,104,183,221,123,4,46,215,51,33,35,181,39,190,21,95,64,81,126, +12,189,187,5,83,57,180,55,255,41,238,236,106,158,183,96,9,33,242,25,30,219,190,25,225,251,80,108,74,227,196,87,32,23,15,142,134,82,127,214,190,151,196,9,100,66,253,30,99,207,103,32,33,182,2,175,196,79,214,12,165,113,34,6,106,197,69,12,112,196,35,172, +60,192,17,23,201,3,35,176,65,229,204,19,44,3,206,154,14,240,182,86,60,194,78,68,206,174,59,40,10,3,177,247,192,84,102,243,30,80,3,138,66,127,255,59,126,0,137,131,124,120,219,151,63,87,0,0,0,0,73,69,78,68,174,66,96,130,0,0 }; + +const char* expandoff_png = (const char*) temp_binary_data_6; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData8.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData8.cpp new file mode 100644 index 00000000..49248ce4 --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData8.cpp @@ -0,0 +1,20 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== expand-on.png ================== +static const unsigned char temp_binary_data_7[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,244,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,0,196,73,68,65,84, +120,1,237,151,209,9,131,48,16,64,239,196,65,92,197,118,131,46,208,17,36,63,109,71,40,126,212,118,132,46,80,58,65,179,138,147,52,77,62,14,36,24,105,76,14,5,239,129,32,158,248,238,148,59,19,60,30,26,3,150,231,235,129,48,128,174,19,92,241,2,4,129,153,75, +167,171,243,77,55,161,56,134,186,32,151,252,11,70,219,211,10,192,168,86,237,239,20,35,111,9,76,12,229,182,178,30,161,120,143,221,151,220,134,167,238,99,220,49,45,199,250,170,234,30,56,18,240,137,145,59,202,156,223,62,70,78,222,108,111,32,182,114,34,91, +2,115,228,89,19,152,35,119,36,207,1,191,3,124,90,181,27,125,238,106,254,134,201,131,40,84,225,191,200,122,64,88,28,214,245,192,20,171,153,3,146,128,32,224,230,119,199,63,81,194,129,48,227,106,116,154,0,0,0,0,73,69,78,68,174,66,96,130,0,0 }; + +const char* expandon_png = (const char*) temp_binary_data_7; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/BinaryData9.cpp b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData9.cpp new file mode 100644 index 00000000..7e31ee46 --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/BinaryData9.cpp @@ -0,0 +1,26 @@ +/* ==================================== JUCER_BINARY_RESOURCE ==================================== + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#include + +namespace InspectorBinaryData +{ + +//================== eyedropper-off.png ================== +static const unsigned char temp_binary_data_8[] = +{ 137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,37,0,0,0,36,8,6,0,0,0,14,194,243,166,0,0,0,9,112,72,89,115,0,0,22,37,0,0,22,37,1,73,82,36,240,0,0,0,1,115,82,71,66,0,174,206,28,233,0,0,0,4,103,65,77,65,0,0,177,143,11,252,97,5,0,0,2,54,73,68,65,84, +120,1,205,152,61,86,194,64,16,199,103,2,246,28,33,150,118,90,218,233,9,196,175,247,172,52,94,64,227,9,132,27,196,19,184,116,22,42,120,2,177,179,147,206,22,111,144,218,247,200,56,179,11,24,48,72,194,38,36,255,247,120,201,110,54,187,63,118,103,103,38,139, +176,38,121,39,254,30,17,93,0,193,30,23,93,254,133,128,48,64,194,142,122,14,84,188,45,194,26,116,126,116,117,139,128,173,133,13,16,20,142,240,70,245,130,80,138,14,148,13,36,34,240,168,70,221,73,177,6,5,202,107,250,46,207,66,47,101,115,119,103,107,247, +107,240,249,62,40,118,166,28,104,102,105,78,200,54,7,5,47,31,15,114,144,237,5,189,9,138,131,226,165,107,76,6,201,170,58,228,32,13,80,135,237,153,202,104,174,156,78,97,46,80,222,169,191,77,35,122,101,136,6,88,10,17,7,114,181,90,62,217,93,26,8,236,129, +180,8,58,114,177,155,41,179,187,114,1,98,207,126,167,186,198,179,219,25,122,4,74,66,5,88,106,12,228,79,203,96,41,49,114,246,198,175,60,245,153,12,155,189,252,144,223,121,97,247,221,83,143,65,63,254,44,143,221,39,91,63,245,18,34,199,57,216,192,182,122, +8,134,139,218,216,27,186,163,13,221,29,87,133,75,95,34,124,251,15,200,10,106,30,136,151,163,141,17,110,230,97,99,43,65,37,1,113,78,212,146,212,131,83,144,125,174,26,130,133,50,67,45,2,154,60,143,156,232,26,126,151,179,120,168,101,64,9,185,211,48,97,196, +191,117,127,154,228,5,116,124,117,29,7,226,251,75,182,177,157,184,141,233,119,230,182,127,146,82,249,169,20,64,23,236,0,85,28,104,146,119,235,96,141,208,226,191,223,87,79,65,170,132,111,41,148,13,208,170,194,170,1,153,126,42,6,100,250,170,24,144,233, +175,98,64,166,207,138,1,137,102,252,84,21,128,76,223,99,141,115,237,15,93,201,233,133,122,186,187,44,3,72,244,59,83,81,44,94,33,118,202,2,18,77,147,60,34,152,249,112,244,206,124,55,250,142,4,168,181,78,32,51,14,36,6,82,73,214,26,179,13,215,3,164,199, +74,113,42,18,162,131,135,105,2,105,94,170,51,144,55,95,201,31,133,125,157,212,35,167,25,35,14,164,207,193,242,52,55,103,168,54,1,221,79,129,204,201,154,7,37,170,38,231,65,114,46,196,247,77,13,212,45,23,72,244,3,136,248,68,216,245,109,26,217,0,0,0,0,73, +69,78,68,174,66,96,130,0,0 }; + +const char* eyedropperoff_png = (const char*) temp_binary_data_8; +} diff --git a/modules/melatonin_inspector/LatestCompiledAssets/InspectorBinaryData.h b/modules/melatonin_inspector/LatestCompiledAssets/InspectorBinaryData.h new file mode 100644 index 00000000..507e1c21 --- /dev/null +++ b/modules/melatonin_inspector/LatestCompiledAssets/InspectorBinaryData.h @@ -0,0 +1,87 @@ +/* ========================================================================================= + + This is an auto-generated file: Any edits you make may be overwritten! + +*/ + +#pragma once + +namespace InspectorBinaryData +{ + extern const char* _DS_Store; + const int _DS_StoreSize = 6148; + + extern const char* clear_png; + const int clear_pngSize = 1419; + + extern const char* dogfoodoff_png; + const int dogfoodoff_pngSize = 929; + + extern const char* dogfoodon_png; + const int dogfoodon_pngSize = 894; + + extern const char* enabledoff_png; + const int enabledoff_pngSize = 1031; + + extern const char* enabledon_png; + const int enabledon_pngSize = 1017; + + extern const char* expandoff_png; + const int expandoff_pngSize = 301; + + extern const char* expandon_png; + const int expandon_pngSize = 303; + + extern const char* eyedropperoff_png; + const int eyedropperoff_pngSize = 673; + + extern const char* eyedropperon_png; + const int eyedropperon_pngSize = 671; + + extern const char* logo_png; + const int logo_pngSize = 25781; + + extern const char* moveoff_png; + const int moveoff_pngSize = 1093; + + extern const char* moveon_png; + const int moveon_pngSize = 958; + + extern const char* search_png; + const int search_pngSize = 941; + + extern const char* speedometeroff_png; + const int speedometeroff_pngSize = 1419; + + extern const char* speedometeron_png; + const int speedometeron_pngSize = 1371; + + extern const char* taboff_png; + const int taboff_pngSize = 531; + + extern const char* tabon_png; + const int tabon_pngSize = 503; + + extern const char* timingoff_png; + const int timingoff_pngSize = 850; + + extern const char* timingon_png; + const int timingon_pngSize = 867; + + // Number of elements in the namedResourceList and originalFileNames arrays. + const int namedResourceListSize = 20; + + // Points to the start of a list of resource names. + extern const char* namedResourceList[]; + + // Points to the start of a list of resource filenames. + extern const char* originalFilenames[]; + + // If you provide the name of one of the binary resource variables above, this function will + // return the corresponding data and its size (or a null pointer if the name isn't found). + const char* getNamedResource (const char* resourceNameUTF8, int& dataSizeInBytes); + + // If you provide the name of one of the binary resource variables above, this function will + // return the corresponding original, non-mangled filename (or a null pointer if the name isn't found). + const char* getNamedResourceOriginalFilename (const char* resourceNameUTF8); +} diff --git a/modules/melatonin_inspector/README.md b/modules/melatonin_inspector/README.md new file mode 100644 index 00000000..4e067f54 --- /dev/null +++ b/modules/melatonin_inspector/README.md @@ -0,0 +1,346 @@ +![](https://github.com/sudara/melatonin_inspector/actions/workflows/ci.yml/badge.svg) + +## Melatonin Component Inspector + +A JUCE module that gives you the ability to inspect and visually edit (non-destructively) components in your UI. + +It's inspired by [Figma](https://figma.com) (where I prefer to design UI), web browser web inspectors and Jim Credland's [Component Debugger](https://github.com/jcredland/juce-toys/blob/master/jcf_debug/source/component_debugger.cpp) [juce-toys](https://github.com/jcredland/juce-toys). + +A big hearty thanks to [Dmytro Kiro](https://github.com/dikadk) and [Roland Rabien (aka FigBug)](https://github.com/figbug) for contributing some great features! + +

+ +

+ + +

+✨
+✨✨
+✨✨✨
+...the features...
+✨✨✨
+✨✨
+✨
+

+ +## Browse and select components visually + +Point n' click to inspect a component, see its size and distance to parent. + +

+ +

+ + +## Explore the component hierarchy + +Immediately gain clarity over parent/child relationships and see what components are currently visible. + +

+ +

+ +Filter components by name. Names are derived from stock components, label/button text, or demangled class names. + + +

+ +

+ + +## Preview Component + +See what exactly is drawing on a per-component basis, even when the component is hidden. A fixed transparency grid helps you understand which components and images have transparency. + +

+ +

+ +## Edit component position and spacing + +There's like...4 different ways to do this, visually and numerically... + +

+ +

+ +We also display component *padding* if you follow the convention of storing them as the component properties `paddingLeft`, `paddingTop`, `paddingRight`, `paddingBottom`. See my `PaddedComponent` [base class as an example](https://gist.github.com/sudara/eed6b8bb3b960b4c2156a0883913ea15). + +

+ +

+ +## Inspect and modify component flags and properties + +See the most important component properties at a glance, including look and feels, fonts for labels, etc. Where applicable, flags are editable! + +

+ +

+ + +Any custom properties you've added the component will also show up here and be editable. + +![AudioPluginHost - 2023-08-14 01](https://github.com/sudara/melatonin_inspector/assets/472/3c69c652-5468-409b-9e3c-134868f4db9c) + +## Nudge components around + +Verify new values, get things pixel perfect. + +

+ +

+ +## View spacing relative to siblings/neighbors + +Hold "alt" while component is selected. A Figma inspired feature. + +

+ +

+ +## Display and modify JUCE widget colors + +No, it's not a christmas miracle, but we do [magically](https://github.com/sudara/melatonin_inspector/blob/main/update_juce_colours.rb) display JUCE's friendly `enum ColourIds` names from the stock widgets. + +See what that Slider's `trackColourId` is set to, and hey, go ahead and try out a new theme in real time. + +(Just to be clear: The color changes are temporary, no code is edited!) + + +![AudioPluginHost - 2023-08-14 38](https://github.com/sudara/melatonin_inspector/assets/472/09e0fdd7-1e31-4956-8808-781920c64a66) + +## Color picker + +Accurately pinpoint colors. Click to pick and store one. Toggle between RGBA and HEX values. + +

+ +

+ + +## FPS meter + +Overlay an FPS meter on your Editor to get an intuitive understanding of your painting performance. Please see the [FAQ](https://github.com/sudara/melatonin_inspector#my-fps-seems-low-is-it-accurate) for details on usage. + +

+ +

+ + +## Display component performance in real time + +A life saving feature. + +See time spent exclusively in a component's `paint` method as well as conveniently provide you with a sum with all children. + +Keep track of the max. Double click to `repaint` and get fresh timings. See [setup paint timing](#7-optional-setup-component-timing). + +![AudioPluginHost - 2023-08-16 57](https://github.com/sudara/melatonin_inspector/assets/472/7b08ea30-ebd1-4900-bb67-02bb8393211b) + + +## Installing with CMake + +### CMake option #1: `FetchContent` + +Place this chunk o love somewhere before your call to `juce_add_plugin` or `juce_add_gui_app`: + +```cmake +Include (FetchContent) +FetchContent_Declare (melatonin_inspector + GIT_REPOSITORY https://github.com/sudara/melatonin_inspector.git + GIT_TAG origin/main + SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/melatonin_inspector) +FetchContent_MakeAvailable (melatonin_inspector) +``` + +### CMake option #2 git submodules + +If you are a git submodule aficionado, life is great! Add this submodule to your project: +```sh +git submodule add -b main https://github.com/sudara/melatonin_inspector.git modules/melatonin_inspector +``` +and then simply call `add_subdirectory` in your CMakeLists.txt. Remember, modules go *before* your main call to `juce_add_plugin` or `juce_add_gui_app` ([this makes life easier in IDEs](https://github.com/juce-framework/JUCE/blob/master/docs/CMake%20API.md#juce_enable_module_source_groups)): + +```cmake +add_subdirectory (modules/melatonin_inspector) +``` + +To update melatonin_inspector down the road (gasp! maintained dependencies!?): +```git +git submodule update --remote --merge modules/melatonin_inspector +``` + +### CMake Step 2: Tell JUCE about the module + +Wait wait, not so fast! You couldn't get away that easily. + +*After* your `juce_add_plugin` call you will need to link your plugin to the module's target, for example: + +``` +target_link_libraries("YourProject" PRIVATE melatonin_inspector) +``` + +Note: you don't have to call `juce_add_module`. That's handled by our CMake. + +If you use Projucer, add the module manually. + +## Installing with Projucer + +If you're rolling old school, or just prefer Projucer life, you'll be happy to note that though JUCE doesn't make it easy we've bent over backwards to make sure our icons, etc are included in the module. + +### Download the module + +You can still use git to add it as a submodule if you'd like stay up to date with any changes: + +``` +git submodule add -b main https://github.com/sudara/melatonin_inspector.git modules/melatonin_inspector +``` + +Or just download it and stick it somewhere. + +### Add it to the projucer + +Just "Add a module from a specified folder" and you're done! + +

+ +

+ +## 2. Add an include to your Plugin Editor + +Include the module header: + +```cpp +#include "melatonin_inspector/melatonin_inspector.h" +``` + +## 3. Add the inspector as a private member to your Editor + +The easiest way to get started is to pass a reference to the root component of your UI (typically the Editor itself like in this example, but you could also inspect anything that derives from `juce::Component`). + +```cpp +melatonin::Inspector inspector { *this }; +``` + +If you prefer the inspector open in the disabled state by default, you can pass false as the second argument. + +```cpp +melatonin::Inspector inspector { *this, false }; +``` + +## 4. Set it visible + +When the inspector as an editor member, you can use `cmd/ctrl i` to toggle whether the inspector is enabled. + +`setVisible` on the member will also pop the window open. + +What I do is have a GUI toggle that pops open the window and enables inspection: + +```cpp +// open the inspector window +inspector.setVisible(true); + +// enable the inspector +inspector.toggle(true); +``` + +## 5. Optional: Make it smarter + +Setting up as above means that the inspector will always be constructed with your editor. Clicking close on the inspector's `DocumentWindow` will just hide it while disabling inspection. + +If you wrap the inspector with `#if DEBUG` this might be fine for you. + +However, if you'd plan to ship a product that includes the inspector, or otherwise want to lazily construct it to be more efficient, use a `unique_ptr` instead and set the `onClose` callback to reset the pointer. + +```c++ +// PluginEditor.h +std::unique_ptr inspector; + +// in some button on-click logic +// replace `this` with a reference to your editor if necessary +if (!inspector) +{ + inspector = std::make_unique (*this); + inspector->onClose = [this]() { inspector.reset(); }; +} + +inspector->setVisible (true); +``` +Thanks to @FigBug for this feature. + +## 6. Optional: Setup component timing + +Just `#include modules/melatonin_inspector/melatonin/helpers/timing.h` and then call the RAII helper ***at the top*** of a component's paint method: + +```c++ +void paint (juce::Graphics& g) override +{ + melatonin::ComponentTimer timer { this }; + + // do all your expensive painting... + ``` + +This simply times the method and stores it in the component's own properties. It will store up to 3 values named `timing1`, `timing2`, `timing3`. + +Want automatic timings for every JUCE component, including stock widgets? [Upvote this FR](https://forum.juce.com/t/fr-callback-or-other-mechanism-for-exposing-component-debugging-timing/54481/1). + +Want timings for your custom components ***right now***? Do what I do and derive all your components from a `juce::Component` subclass which wraps the `paint` call and adds the helper before `paint` is called. + +Check out [the forum post for detail](https://forum.juce.com/t/fr-callback-or-other-mechanism-for-exposing-component-debugging-timing/54481/11?u=sudara). Or, if you run a JUCE fork, you might prefer [Roland's solution](https://forum.juce.com/t/fr-callback-or-other-mechanism-for-exposing-component-debugging-timing/54481/6?u=sudara). + +## FAQ + +### Can I use this in a GUI app/standalone? + +Yup! See the tests folder for an example. + +### Can I save my component resizes or edits? + +Nope! + +For that, one would need a component system relying on data for placement and size vs. code. See [Daniel's Foley GUI Magic](https://github.com/ffAudio/foleys_gui_magic). + +### How is the component hierarchy created? + +It traverses components from the root, building a `TreeView`. + +In the special case of `TabbedComponent`, each tab is added as a child. + +### My FPS seems low, is it accurate? + +It's a smoothed running average. + +If you see low FPS rates, check the following: + +* Are you running *just* your plugin? Make sure other plugin UIs are closed. +* I optimize the inspector for Debug usage, but it can be tough for complex UIs to hit 60fps in Debug, especially on macOS (see note below). See what happens in Release. +* You might have legitimately expensive paint calls (esp. in Debug). You can verify this out via [Perfetto](https://github.com/sudara/melatonin_perfetto). + +On recent macOS, a `repaint()` on even small sections of a window (ie, what the FPS meter does) will cause the OS to paint the entire plugin window. You can use `Flash Screen Updates` in Quartz Debug to verify this. Because of this macOS behavior, the FPS meter will actually trigger full repaints of your UI, so anything expensive (especially in Debug) will slow down what the FPS meter reports. + +If you are using the JUCE flag `JUCE_COREGRAPHICS_RENDER_WITH_MULTIPLE_PAINT_CALLS`, JUCE will internally manage the rectangles that need to be repainted, with the aim of being more precise/hygenic with what actually gets painted. This might be a good choice if your plugin already frequently repainting parts of the UI. But please don't switch over to that flag just to appease the FPS meter! It needs to be a choice you make depending on your internal testing (without the FPS meter in play). + + +Feel free to ask for other ideas in the [forum thread](https://forum.juce.com/t/melatonin-inspector-a-web-inspector-ish-module-for-juce-components/45672). + +### I have a problem / feature request + +Please do submit an Issue or PR on the repository! Or visit the [official thread on the JUCE forum](https://forum.juce.com/t/melatonin-inspector-a-web-inspector-ish-module-for-juce-components/45672). + + +## Contributing + +Contributions are always welcome! + +The inspector wouldn't be half as awesome without the help of the community. + +If you'd like to contribute, look out for the issues tagged with "[Good First Issue](https://github.com/sudara/melatonin_inspector/issues?q=is:issue+is:open+label:%22good+first+issue%22)" + +Note that CI tests for compilation and treats errors on both macOS and Windows as errors. + +### Assets + +All assets are PNG exported at 2x. + +Please see the CMakelists.txt file for details on how to add icons in a Projucer friendly way. There's a script for it! diff --git a/modules/melatonin_inspector/copy_cmake_assets.rb b/modules/melatonin_inspector/copy_cmake_assets.rb new file mode 100755 index 00000000..515cad51 --- /dev/null +++ b/modules/melatonin_inspector/copy_cmake_assets.rb @@ -0,0 +1,55 @@ +#!/usr/bin/env ruby + +# Note, this needs to be run from your PROJECT directory, not the module's directory +# e.g. +# modules/melatonin_inspector/copy_cmake_assets.rb +# +# You can run it over and over without worry... + +require 'fileutils' + +# you might need to change if you're not Sudara.... +build_dir = "cmake-build-debug" +module_dir = "modules/melatonin_inspector/" + +# you shouldn't have to change anyhting below. File an issue if you do. +source_build_path = File.join module_dir, "juce_binarydata_MelatoninInspectorAssets/JUCELibraryCode" +source_dir = File.join(build_dir, source_build_path) +target_cpp_dir = File.join module_dir, "LatestCompiledAssets" +mela_cpp_file = File.join module_dir, "melatonin_inspector.cpp" +target_h_file = File.join target_cpp_dir, "InspectorBinaryData.h" + +if !File.directory? source_dir + puts "source_dir not found, check the build_dir: " + source_dir +elsif !File.directory? target_cpp_dir + puts "target_cpp_dir not found, check it: " + target_cpp_dir +else + + puts "Removing existing *.cpp and InspectorBinaryData.h" + FileUtils.rm_rf Dir.glob "#{target_cpp_dir}/*.cpp" + FileUtils.rm_f target_h_file + + puts "Copying over the newly generated assets" + FileUtils.cp Dir["#{source_dir}/*.cpp"], "#{target_cpp_dir}/" + + puts "Coping over InspectorBinaryData.h" + FileUtils.cp "#{source_dir}/InspectorBinaryData.h", target_h_file + + puts "Rewriting #{mela_cpp_file} with new includes" + cpp_file = File.open mela_cpp_file, 'w' + cpp_file.write "// WARNING! This file is automatically written by copy_cmake_assets.rb\n\n" + cpp_file.write "// As recommended by the JUCE MODULE API, these cpp files are included by the main module cpp\n" + cpp_file.write "// See https://github.com/juce-framework/JUCE/blob/master/docs/JUCE%20Module%20Format.md#module-cpp-files\n\n" + cpp_file.write "#include \n" + cpp_file.write "JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE(\"-Wredundant-decls\")\n" + cpp_file.write "// NOLINTBEGIN(bugprone-suspicious-include)\n" + + Dir["#{target_cpp_dir}/*.cpp"].each do |file| + cpp_file.write "#include \"LatestCompiledAssets/#{File.basename(file)}\"\n" + end + cpp_file.write "// NOLINTEND(bugprone-suspicious-include)\n" + cpp_file.write "JUCE_END_IGNORE_WARNINGS_GCC_LIKE\n" + cpp_file.close +end + +puts "All done." diff --git a/modules/melatonin_inspector/melatonin/component_model.h b/modules/melatonin_inspector/melatonin/component_model.h new file mode 100644 index 00000000..b09d3161 --- /dev/null +++ b/modules/melatonin_inspector/melatonin/component_model.h @@ -0,0 +1,459 @@ +#pragma once + +#include +#include "helpers/component_helpers.h" +#include "juce_gui_basics/juce_gui_basics.h" + +namespace melatonin +{ + class ComponentModel : private juce::Value::Listener, private juce::ComponentListener + { + public: + class Listener + { + public: + virtual ~Listener() = default; + virtual void componentModelChanged (ComponentModel& model) = 0; + }; + + juce::Value nameValue; + juce::Value widthValue, heightValue, xValue, yValue; + juce::Value enabledValue, opaqueValue, hasCachedImageValue, accessibilityHandledValue; + juce::Value visibleValue, wantsFocusValue, interceptsMouseValue, childrenInterceptsMouseValue; + juce::Value lookAndFeelValue, typeValue, fontValue, alphaValue; + juce::Value pickedColor; + juce::Value timing1, timing2, timing3, timingMax, hasChildren; + + juce::Value isToggleable, toggleState, clickTogglesState, radioGroupId; + + struct AccessiblityDetail + { + juce::Value title, value, role, handlerType; + } accessiblityDetail; + + double timingWithChildren1, timingWithChildren2, timingWithChildren3, timingWithChildrenMax; + + ComponentModel() = default; + + ~ComponentModel() override + { + selectComponent (nullptr); + } + + void selectComponent (juce::Component* component) + { + TRACE_COMPONENT(); + + // add component listener to component and unsubscribe from previous component + if (selectedComponent) + selectedComponent->removeComponentListener (this); + + selectedComponent = component; + + if (selectedComponent) + selectedComponent->addComponentListener (this); + + updateModel(); + } + + void deselectComponent() + { + TRACE_COMPONENT(); + + if (selectedComponent) + selectedComponent->removeComponentListener (this); + + selectedComponent = nullptr; + updateModel(); + } + + struct NamedProperty + { + NamedProperty() = default; + NamedProperty (juce::String n, const juce::var& v) + : name (std::move (n)), value (v) + { + } + + juce::String name; + juce::Value value; + }; + + std::vector namedProperties; + std::vector colors; + + void refresh() + { + updateModel(); + } + + void removeListener (Listener& listener) + { + listenerList.remove (&listener); + } + + void addListener (Listener& listener) + { + listenerList.add (&listener); + } + + // this may return nullptr if no component is selected + juce::Component* getSelectedComponent() + { + return selectedComponent; + } + + [[nodiscard]] bool hasPerformanceTiming() + { + return timing1.getValue().isDouble(); + } + + private: + juce::ListenerList listenerList; + juce::Component::SafePointer selectedComponent; + + void updateModel() + { + TRACE_COMPONENT(); + + removeListeners(); + + // always show picked color, even with no component selected + if (!pickedColor.getValue().isVoid()) + colors.emplace_back ("Last Picked", pickedColor); + + if (!selectedComponent) + { + // if not manually removed, it'll linger in the model... + removePerformanceData(); + notifyListeners(); + return; + } + + nameValue = selectedComponent->getName(); + lookAndFeelValue = lnfString (selectedComponent); + visibleValue = selectedComponent->isVisible(); + enabledValue = selectedComponent->isEnabled(); + alphaValue = juce::String (selectedComponent->getAlpha()); + opaqueValue = selectedComponent->isOpaque(); + wantsFocusValue = selectedComponent->getWantsKeyboardFocus(); + fontValue = componentFontValue (selectedComponent); + hasCachedImageValue = selectedComponent->getCachedComponentImage() != nullptr; + typeValue = type (*selectedComponent); + accessibilityHandledValue = selectedComponent->isAccessible(); + + if (auto button = dynamic_cast (selectedComponent.getComponent())) + { + isToggleable = button->isToggleable(); + toggleState = button->getToggleState(); + clickTogglesState = button->getClickingTogglesState(); + radioGroupId = button->getRadioGroupId(); + } + + nameValue.addListener(this); + widthValue.addListener (this); + heightValue.addListener (this); + xValue.addListener (this); + yValue.addListener (this); + visibleValue.addListener (this); + wantsFocusValue.addListener (this); + enabledValue.addListener (this); + opaqueValue.addListener (this); + alphaValue.addListener (this); + accessibilityHandledValue.addListener (this); + interceptsMouseValue.addListener (this); + childrenInterceptsMouseValue.addListener (this); + + isToggleable.addListener (this); + toggleState.addListener (this); + clickTogglesState.addListener (this); + radioGroupId.addListener (this); + + if (selectedComponent->isAccessible() && selectedComponent->getAccessibilityHandler()) + { + auto* accH = selectedComponent->getAccessibilityHandler(); + accessiblityDetail.handlerType = type (*accH); + if (accH->getValueInterface()) + { + accessiblityDetail.value = accH->getValueInterface()->getCurrentValueAsString(); + } + else + { + accessiblityDetail.value = "no value interface"; + } + accessiblityDetail.title = accH->getTitle(); + auto role = accH->getRole(); + switch (role) + { + // Amazingly juce doesn' thave a display name fn for these +#define DN(x) \ + case juce::AccessibilityRole::x: \ + accessiblityDetail.role = #x; \ + break; + DN (button) + DN (toggleButton) + DN (radioButton) + DN (comboBox) + DN (image) + DN (slider) + DN (label) + DN (staticText) + DN (editableText) + DN (menuItem) + DN (menuBar) + DN (popupMenu) + DN (table) + DN (tableHeader) + DN (column) + DN (row) + DN (cell) + DN (hyperlink) + DN (list) + DN (listItem) + DN (tree) + DN (treeItem) + DN (progressBar) + DN (group) + DN (dialogWindow) + DN (window) + DN (scrollBar) + DN (tooltip) + DN (splashScreen) + DN (ignored) + DN (unspecified) +#undef DN + default: + accessiblityDetail.role = juce::String ("Unknown ") + juce::String ((int) role); + break; + } + } + + { + bool interceptsMouse = false; + bool childrenInterceptsMouse = false; + selectedComponent->getInterceptsMouseClicks (interceptsMouse, childrenInterceptsMouse); + interceptsMouseValue = interceptsMouse; + childrenInterceptsMouseValue = childrenInterceptsMouse; + } + + hasChildren.setValue (selectedComponent->getNumChildComponents() > 0); + populatePerformanceData (selectedComponent->getProperties()); + + { + auto& properties = selectedComponent->getProperties(); + for (const auto& nv : properties) + { + if (nv.name.toString().startsWith ("jcclr_")) + colors.emplace_back (nv.name.toString(), nv.value); + else + namedProperties.emplace_back (nv.name.toString(), nv.value); + } + + for (auto& nv : namedProperties) + nv.value.addListener (this); + + for (auto& nv : colors) + nv.value.addListener (this); + } + notifyListeners(); + } + + void removeListeners() + { + TRACE_COMPONENT(); + + widthValue.removeListener (this); + heightValue.removeListener (this); + xValue.removeListener (this); + yValue.removeListener (this); + enabledValue.removeListener (this); + opaqueValue.removeListener (this); + alphaValue.removeListener (this); + visibleValue.removeListener (this); + wantsFocusValue.removeListener (this); + accessibilityHandledValue.removeListener (this); + interceptsMouseValue.removeListener (this); + childrenInterceptsMouseValue.removeListener (this); + + isToggleable.removeListener (this); + toggleState.removeListener (this); + clickTogglesState.removeListener (this); + radioGroupId.removeListener (this); + + for (auto& np : namedProperties) + np.value.removeListener (this); + + for (auto& np : colors) + np.value.removeListener (this); + + colors.clear(); + namedProperties.clear(); + } + + // allows properties to be set from our properties + void valueChanged (juce::Value& value) override + { + TRACE_COMPONENT(); + + if (selectedComponent) + { + if (value.refersToSameSourceAs (widthValue) || value.refersToSameSourceAs (heightValue)) + { + selectedComponent->setSize ((int) widthValue.getValue(), (int) heightValue.getValue()); + } + else if (value.refersToSameSourceAs (xValue) || value.refersToSameSourceAs (yValue)) + { + int leftVal = xValue.getValue(); + int topVal = yValue.getValue(); + + // in cases where components are animated or moved via AffineTransforms + // we can get a feedback loop, as the left/top values are no longer + // the actual position in the component + // so first remove any transform present + selectedComponent->setTransform (juce::AffineTransform()); + selectedComponent->setTopLeftPosition (leftVal, topVal); + } + else if (value.refersToSameSourceAs (visibleValue)) + { + selectedComponent->setVisible (visibleValue.getValue()); + } + else if (value.refersToSameSourceAs (wantsFocusValue)) + { + selectedComponent->setWantsKeyboardFocus (wantsFocusValue.getValue()); + } + else if (value.refersToSameSourceAs (enabledValue)) + { + selectedComponent->setEnabled (enabledValue.getValue()); + } + else if (value.refersToSameSourceAs (alphaValue)) + { + selectedComponent->setAlpha ((float) alphaValue.getValue()); + } + else if (value.refersToSameSourceAs (opaqueValue)) + { + selectedComponent->setOpaque (opaqueValue.getValue()); + } + else if (value.refersToSameSourceAs (accessibilityHandledValue)) + { + selectedComponent->setAccessible (accessibilityHandledValue.getValue()); + } + else if (value.refersToSameSourceAs (interceptsMouseValue) || value.refersToSameSourceAs (childrenInterceptsMouseValue)) + { + selectedComponent->setInterceptsMouseClicks (interceptsMouseValue.getValue(), childrenInterceptsMouseValue.getValue()); + } + else if (value.refersToSameSourceAs (isToggleable)) + { + if (auto button = dynamic_cast (selectedComponent.getComponent())) + button->setToggleable (isToggleable.getValue()); + } + else if (value.refersToSameSourceAs (toggleState)) + { + if (auto button = dynamic_cast (selectedComponent.getComponent())) + button->setToggleState (toggleState.getValue(), juce::dontSendNotification); + } + else if (value.refersToSameSourceAs (clickTogglesState)) + { + if (auto button = dynamic_cast (selectedComponent.getComponent())) + button->setClickingTogglesState (clickTogglesState.getValue()); + } + else if (value.refersToSameSourceAs (radioGroupId)) + { + if (auto button = dynamic_cast (selectedComponent.getComponent())) + button->setRadioGroupId (radioGroupId.getValue()); + } + else + { + for (auto& nv : namedProperties) + { + if (value.refersToSameSourceAs (nv.value)) + { + selectedComponent->getProperties().set (nv.name, nv.value.getValue()); + selectedComponent->repaint(); + break; + } + } + + for (auto& nv : colors) + { + if (value.refersToSameSourceAs (nv.value)) + { + selectedComponent->getProperties().set (nv.name, nv.value.getValue()); + selectedComponent->repaint(); + break; + } + } + } + } + else + { + jassertfalse; + } + } + + void componentMovedOrResized (juce::Component&, bool wasMoved, bool wasResized) override + { + TRACE_COMPONENT(); + + if (wasResized || wasMoved) + { + updateModel(); + } + } + + void populatePerformanceData (const juce::NamedValueSet& props) + { + if (props.contains ("timing1")) + { + // assume they are all there + timing1 = props["timing1"]; + timing2 = props["timing2"]; + timing3 = props["timing3"]; + timingMax = props["timingMax"]; + + timingWithChildren1 = timing1.getValue(); + timingWithChildren2 = timing2.getValue(); + timingWithChildren3 = timing3.getValue(); + timingWithChildrenMax = timingMax.getValue(); + getTimingWithChildren (selectedComponent); + } + else + { + removePerformanceData(); + } + } + + void notifyListeners() + { + listenerList.call ([this] (Listener& listener) { + listener.componentModelChanged (*this); + }); + } + + void removePerformanceData() + { + timing1 = juce::var(); + timing2 = juce::var(); + timing3 = juce::var(); + timingMax = juce::var(); + timingWithChildren1 = juce::var(); + timingWithChildren2 = juce::var(); + timingWithChildren3 = juce::var(); + timingWithChildrenMax = juce::var(); + } + + void getTimingWithChildren (juce::Component* component) + { + for (auto child : component->getChildren()) + { + if (child->getProperties().contains ("timing1")) + { + timingWithChildren1 += (double) child->getProperties()["timing1"]; + timingWithChildren2 += (double) child->getProperties()["timing2"]; + timingWithChildren3 += (double) child->getProperties()["timing3"]; + timingWithChildrenMax += (double) child->getProperties()["timingMax"]; + getTimingWithChildren (child); + } + } + } + }; +} diff --git a/modules/melatonin_inspector/melatonin/components/accesibility.h b/modules/melatonin_inspector/melatonin/components/accesibility.h new file mode 100644 index 00000000..1b3360c8 --- /dev/null +++ b/modules/melatonin_inspector/melatonin/components/accesibility.h @@ -0,0 +1,62 @@ + +#pragma once +#include "melatonin_inspector/melatonin/component_model.h" + +namespace melatonin +{ + class Accessibility : public juce::Component, private ComponentModel::Listener + { + public: + explicit Accessibility (ComponentModel& m) : model (m) + { + addAndMakeVisible (&panel); + model.addListener (*this); + } + + ~Accessibility() override + { + model.removeListener (*this); + } + + void updateProperties() + { + panel.clear(); + + if (!model.getSelectedComponent()) + return; + + auto& ad = model.accessiblityDetail; + auto aprops = juce::Array { + new juce::TextPropertyComponent (ad.title, "Title", 200, false, false), + new juce::TextPropertyComponent (ad.value, "Value", 200, false, false), + new juce::TextPropertyComponent (ad.role, "Role", 200, false, false), + new juce::TextPropertyComponent (ad.handlerType, "Handler", 200, false, false), + }; + for (auto* p : aprops) + { + p->setLookAndFeel (&getLookAndFeel()); + } + panel.addProperties (aprops, 0); + resized(); + } + + protected: + ComponentModel& model; + + void componentModelChanged (ComponentModel&) override + { + updateProperties(); + } + + void resized() override + { + TRACE_COMPONENT(); + // let the property panel know what total height we need to be + panel.setBounds (getLocalBounds().withTrimmedTop (padding)); + } + + int padding { 3 }; + + juce::PropertyPanel panel { "Accessibility" }; + }; +} diff --git a/modules/melatonin_inspector/melatonin/components/box_model.h b/modules/melatonin_inspector/melatonin/components/box_model.h new file mode 100644 index 00000000..6b7f35fa --- /dev/null +++ b/modules/melatonin_inspector/melatonin/components/box_model.h @@ -0,0 +1,345 @@ +#pragma once +#include "../helpers/colors.h" +#include "../helpers/component_helpers.h" +#include "collapsable_panel.h" +#include "melatonin_inspector/melatonin/component_model.h" + +namespace melatonin +{ + class BoxModel : public juce::Component, + private juce::Label::Listener, + private ComponentModel::Listener + { + public: + explicit BoxModel (ComponentModel& componentModel) : model (componentModel) + { + addAndMakeVisible (componentLabel); + componentLabel.setColour (juce::Label::textColourId, colors::boxModelBoundingBox); + componentLabel.setJustificationType (juce::Justification::centredLeft); + + addAndMakeVisible (parentComponentLabel); + parentComponentLabel.setJustificationType (juce::Justification::centredLeft); + + addAndMakeVisible (widthLabel); + widthLabel.addListener (this); + widthLabel.setFont (20.0f); + widthLabel.setJustificationType (juce::Justification::centredRight); + widthLabel.setColour (juce::Label::textColourId, colors::white); + + addAndMakeVisible (byLabel); + byLabel.setText (juce::CharPointer_UTF8("\xc3\x97"), juce::dontSendNotification); + byLabel.setFont (20.f); + byLabel.setJustificationType (juce::Justification::centred); + byLabel.setColour (juce::Label::textColourId, colors::white); + + addAndMakeVisible (heightLabel); + heightLabel.addListener (this); + heightLabel.setFont (20.0f); + heightLabel.setJustificationType (juce::Justification::centredLeft); + heightLabel.setColour (juce::Label::textColourId, colors::white); + + juce::Label* parentLabels[4] = { &topToParentLabel, &rightToParentLabel, &bottomToParentLabel, &leftToParentLabel }; + juce::Label* paddingLabels[4] = { &paddingTopLabel, &paddingRightLabel, &paddingLeftLabel, &paddingBottomLabel }; + + for (auto parentLabel : parentLabels) + { + addAndMakeVisible (parentLabel); + parentLabel->setText ("-", juce::dontSendNotification); + parentLabel->setJustificationType (juce::Justification::centred); + parentLabel->addListener (this); + + // centers juce::TextEditor (hack since juce::Label is not doing it by default) + parentLabel->onEditorShow = [parentLabel] { + if (auto editor = parentLabel->getCurrentTextEditor()) + { + auto labelJustification = parentLabel->getJustificationType(); + if (editor->getJustificationType() != labelJustification) + { + editor->setJustification (parentLabel->getJustificationType()); + } + } + }; + } + + for (auto l : paddingLabels) + { + addChildComponent (l); + l->setText ("-", juce::dontSendNotification); + l->setJustificationType (juce::Justification::centred); + l->setColour (juce::TextEditor::ColourIds::highlightColourId, colors::boxModelBoundingBox.darker()); + + // centers juce::TextEditor (hack since juce::Label is not doing it by default) + l->onEditorShow = [l] { + if (auto editor = l->getCurrentTextEditor()) + { + auto labelJustification = l->getJustificationType(); + if (editor->getJustificationType() != labelJustification) + { + editor->setJustification (l->getJustificationType()); + } + } + }; + + l->onEditorHide = [l] { + auto text = l->getText (true); + if (text.getIntValue() == 0) + l->setText ("0", juce::dontSendNotification); + }; + } + + model.addListener (*this); + } + + ~BoxModel() override + { + model.removeListener (*this); + } + + void paint (juce::Graphics& g) override + { + // dashed line rectangles be hard, yo! + g.setColour (colors::label.darker (0.6f)); + float dashLengths[2] = { 2.f, 2.f }; + parentRectanglePath.clear(); + parentRectanglePath.addRectangle (parentComponentRectangle()); + auto parentStroke = juce::PathStrokeType (0.5); + parentStroke.createDashedStroke (parentRectanglePath, parentRectanglePath, dashLengths, 2); + g.strokePath (parentRectanglePath, parentStroke); + + g.setColour (colors::boxModelBoundingBox); + g.drawRect (componentRectangle(), 1); + + // draw padding + g.setColour (colors::boxModelBoundingBox.withAlpha (0.2f)); + g.drawRect (componentRectangle(), (int) padding); + } + + void resized() override + { + auto bounds = parentComponentRectangle(); + auto center = bounds.getCentre(); + auto labelHeight = 30; + + parentComponentLabel.setBounds (bounds.getX(), bounds.getY() - labelHeight + 4, bounds.getWidth(), labelHeight); + componentLabel.setBounds (componentRectangle().getX(), componentRectangle().getY() - labelHeight + 4, componentRectangle().getWidth(), labelHeight); + + widthLabel.setBounds (center.getX() - 10 - paddingToParent, center.getY() - 15, paddingToParent, labelHeight); + byLabel.setBounds (center.getX() - 10, center.getY() - 15, 20, labelHeight); + heightLabel.setBounds (center.getX() + 10, center.getY() - 15, paddingToParent, labelHeight); + + topToParentLabel.setBounds (center.getX() - paddingToParent / 2, padding + paddingToParent / 2 - labelHeight / 2 - 3, paddingToParent, labelHeight); + rightToParentLabel.setBounds (getWidth() - padding - paddingToParent / 2 - paddingToParent / 2, center.getY() - labelHeight / 2, paddingToParent, labelHeight); + bottomToParentLabel.setBounds (center.getX() - paddingToParent / 2, getHeight() - padding - paddingToParent / 2 - labelHeight / 2 + 3, paddingToParent, labelHeight); + leftToParentLabel.setBounds (padding + paddingToParent / 2 - paddingToParent / 2, center.getY() - labelHeight / 2, paddingToParent, labelHeight); + + auto area1 = bounds.reduced (paddingToParent) + .removeFromTop (padding) + .withSizeKeepingCentre (padding, padding); + paddingTopLabel.setBounds (area1); + + auto area2 = bounds.reduced (paddingToParent) + .removeFromBottom (padding) + .withSizeKeepingCentre (padding, padding); + paddingBottomLabel.setBounds (area2); + + auto area3 = bounds.reduced (paddingToParent) + .removeFromLeft (padding) + .withSizeKeepingCentre (padding, padding); + paddingLeftLabel.setBounds (area3); + + auto area4 = bounds.reduced (paddingToParent) + .removeFromRight (padding) + .withTrimmedTop (padding) + .withTrimmedBottom (padding) + .withSizeKeepingCentre (padding, padding); + paddingRightLabel.setBounds (area4); + } + + private: + ComponentModel& model; + + juce::Label componentLabel; + juce::Label parentComponentLabel; + + juce::Label widthLabel; + juce::Label byLabel; + juce::Label heightLabel; + + juce::Label topToParentLabel; + juce::Label rightToParentLabel; + juce::Label bottomToParentLabel; + juce::Label leftToParentLabel; + + juce::Label paddingTopLabel, + paddingRightLabel, + paddingBottomLabel, + paddingLeftLabel; + + int padding = 34; + int paddingToParent = 44; + juce::Path parentRectanglePath; // complicated b/c it's dashed + bool isPaddingComponent { false }; + + void labelTextChanged (juce::Label* changedLabel) override + { + + if (changedLabel == &widthLabel || changedLabel == &heightLabel) + { + model.getSelectedComponent()->setSize (widthLabel.getText().getIntValue(), heightLabel.getText().getIntValue()); + } + if (changedLabel == &paddingRightLabel || changedLabel == &paddingLeftLabel + || changedLabel == &paddingTopLabel || changedLabel == &paddingBottomLabel) + { + updateDisplayedComponentPaddingProperties (paddingRightLabel.getText().getIntValue(), paddingLeftLabel.getText().getIntValue(), paddingTopLabel.getText().getIntValue(), paddingBottomLabel.getText().getIntValue()); + } + if (changedLabel == &topToParentLabel || changedLabel == &bottomToParentLabel + || changedLabel == &leftToParentLabel || changedLabel == &rightToParentLabel) + { + auto topVal = topToParentLabel.getText().getIntValue(); + auto leftVal = leftToParentLabel.getText().getIntValue(); + auto bottomVal = bottomToParentLabel.getText().getIntValue(); + auto rightVal = rightToParentLabel.getText().getIntValue(); + model.getSelectedComponent()->setTopLeftPosition (leftVal, topVal); + model.getSelectedComponent()->setSize (model.getSelectedComponent()->getParentWidth() - rightVal - leftVal, + model.getSelectedComponent()->getParentHeight() - bottomVal - topVal); + } + } + + void componentModelChanged (ComponentModel&) override + { + updateLabels(); + updatePaddingLabelsIfNeeded(); + } + + juce::Rectangle parentComponentRectangle() + { + return getLocalBounds().reduced (padding); + } + + juce::Rectangle componentRectangle() + { + return parentComponentRectangle().reduced (paddingToParent); + } + + void updateLabels() + { + if (!model.getSelectedComponent()) + { + reset(); + return; + } + auto boundsInParent = model.getSelectedComponent()->getBoundsInParent(); + + parentComponentLabel.setText (componentString (model.getSelectedComponent()->getParentComponent()), juce::dontSendNotification); + componentLabel.setText (componentString (model.getSelectedComponent()), juce::dontSendNotification); + + widthLabel.setText (juce::String (model.getSelectedComponent()->getWidth()), juce::dontSendNotification); + heightLabel.setText (juce::String (model.getSelectedComponent()->getHeight()), juce::dontSendNotification); + + widthLabel.setEditable (true); + heightLabel.setEditable (true); + + topToParentLabel.setText (juce::String (boundsInParent.getY()), juce::dontSendNotification); + topToParentLabel.setEditable (true); + + rightToParentLabel.setText (juce::String (model.getSelectedComponent()->getParentWidth() - model.getSelectedComponent()->getWidth() - boundsInParent.getX()), juce::dontSendNotification); + rightToParentLabel.setEditable (true); + + bottomToParentLabel.setText (juce::String (model.getSelectedComponent()->getParentHeight() - model.getSelectedComponent()->getHeight() - boundsInParent.getY()), juce::dontSendNotification); + bottomToParentLabel.setEditable (true); + + leftToParentLabel.setText (juce::String (boundsInParent.getX()), juce::dontSendNotification); + leftToParentLabel.setEditable (true); + + repaint(); + } + + // See Melatonin's PaddedComponent or store this info in your component's getProperties + void updatePaddingLabelsIfNeeded() + { + if (!model.getSelectedComponent()) + { + // if model.getSelectedComponent() is null, getting props will fail + juce::Label* paddingLabels[4] = { &paddingTopLabel, &paddingRightLabel, &paddingLeftLabel, &paddingBottomLabel }; + + for (auto pl : paddingLabels) + { + pl->setText ("-", juce::dontSendNotification); + pl->setEditable (false); + pl->removeListener (this); + } + + return; + } + auto component = model.getSelectedComponent(); + auto props = component->getProperties(); + auto hasTopPadding = props.contains ("paddingTop"); + auto hasBottomPadding = props.contains ("paddingBottom"); + auto hasLeftPadding = props.contains ("paddingLeft"); + auto hasRightPadding = props.contains ("paddingRight"); + + int paddingTop = props["paddingTop"]; + int paddingBottom = props["paddingBottom"]; + int paddingLeft = props["paddingLeft"]; + int paddingRight = props["paddingRight"]; + + isPaddingComponent = hasBottomPadding || hasTopPadding || hasLeftPadding || hasRightPadding; + paddingTopLabel.setVisible (isPaddingComponent); + paddingBottomLabel.setVisible (isPaddingComponent); + paddingLeftLabel.setVisible (isPaddingComponent); + paddingRightLabel.setVisible (isPaddingComponent); + + paddingTopLabel.setText (hasTopPadding ? juce::String (paddingTop) : "-", juce::dontSendNotification); + paddingTopLabel.setEditable (hasTopPadding); + paddingTopLabel.addListener (this); + + paddingBottomLabel.setText (hasBottomPadding ? juce::String (paddingBottom) : "-", juce::dontSendNotification); + paddingBottomLabel.setEditable (hasBottomPadding); + paddingBottomLabel.addListener (this); + + paddingLeftLabel.setText (hasLeftPadding ? juce::String (paddingLeft) : "-", juce::dontSendNotification); + paddingLeftLabel.setEditable (hasLeftPadding); + paddingLeftLabel.addListener (this); + + paddingRightLabel.setText (hasRightPadding ? juce::String (paddingRight) : "-", juce::dontSendNotification); + paddingRightLabel.setEditable (hasRightPadding); + paddingRightLabel.addListener (this); + } + + void updateDisplayedComponentPaddingProperties (double paddingRight, double paddingLeft, double paddingTop, double paddingBottom) + { + if (model.getSelectedComponent()) + { + auto& props = model.getSelectedComponent()->getProperties(); + props.set ("paddingLeft", paddingLeft); + props.set ("paddingTop", paddingTop); + props.set ("paddingRight", paddingRight); + props.set ("paddingBottom", paddingBottom); + model.getSelectedComponent()->resized(); + model.getSelectedComponent()->repaint(); + } + } + + void reset() + { + juce::Label* labels[6] = { &widthLabel, &heightLabel, &topToParentLabel, &rightToParentLabel, &bottomToParentLabel, &leftToParentLabel }; + + for (auto label : labels) + { + label->setText ("-", juce::dontSendNotification); + label->setEditable (false); + } + + juce::Label* paddingLabels[4] = { &paddingTopLabel, &paddingRightLabel, &paddingLeftLabel, &paddingBottomLabel }; + for (auto label : paddingLabels) + { + label->setVisible (false); + } + + componentLabel.setText ("", juce::dontSendNotification); + parentComponentLabel.setText ("", juce::dontSendNotification); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BoxModel) + }; +} diff --git a/modules/melatonin_inspector/melatonin/components/collapsable_panel.h b/modules/melatonin_inspector/melatonin/components/collapsable_panel.h new file mode 100644 index 00000000..782439a2 --- /dev/null +++ b/modules/melatonin_inspector/melatonin/components/collapsable_panel.h @@ -0,0 +1,118 @@ +#pragma once +#include "../helpers/colors.h" +#include "../helpers/inspector_settings.h" +#include + +#include + +namespace melatonin +{ + // disclosure triangle component with toggle button to show/hide piece of content + class CollapsablePanel : public juce::Component + { + public: + explicit CollapsablePanel (juce::String n, juce::Component* c, bool d = false) : name (std::move (n)), drawTopDivider (d), content (c) + { + toggleButton.setLookAndFeel (&toggleButtonLookAndFeel); + addAndMakeVisible (toggleButton); + addChildComponent (content); + toggleButton.setButtonText (name); + toggleButton.onClick = [this] { + toggle (toggleButton.getToggleState()); + }; + } + void paint (juce::Graphics& g) override + { + if (drawTopDivider) + { + g.setColour (colors::panelLineSeparator); + g.drawHorizontalLine (0, 0, (float) getWidth()); + } + } + + void resized() override + { + auto area = getLocalBounds(); + if (drawTopDivider) + area.removeFromTop (1); // pixel perfect, please + + toggleButton.setBounds (area.reduced (8, 2).removeFromLeft (200)); + } + + // when the inspector as a whole is toggled, recall our content's visibility + void visibilityChanged() override + { + if (isVisible()) + toggle (settings->props->getBoolValue (name, true)); + } + + // called when panel is toggled or overall inspector is toggled + void toggle (bool enabled) + { + settings->props->setValue (name, enabled); + content->setVisible (enabled); + toggleButton.setToggleState (enabled, juce::dontSendNotification); + if (getParentComponent()) + getParentComponent()->resized(); + } + + private: + struct ToggleButtonLnF : juce::LookAndFeel_V4 + { + ToggleButtonLnF() + { + setColour (juce::ToggleButton::textColourId, colors::disclosure); + setColour (juce::ToggleButton::tickColourId, colors::disclosure); + } + + void drawToggleButton (juce::Graphics& g, juce::ToggleButton& button, bool shouldDrawButtonAsHighlighted, bool shouldDrawButtonAsDown) override + { + auto font = juce::Font ("Verdana", 14.5, juce::Font::FontStyleFlags::plain).withExtraKerningFactor (0.1f); + auto tickWidth = font.getHeight(); + + drawTickBox (g, button, 6.0f, ((float) button.getHeight() - tickWidth) * 0.5f, tickWidth, tickWidth, button.getToggleState(), button.isEnabled(), shouldDrawButtonAsHighlighted, shouldDrawButtonAsDown); + + g.setColour (button.findColour (juce::ToggleButton::textColourId)); + g.setFont (font); + + if (!button.isEnabled()) + g.setOpacity (0.5f); + + g.drawText (button.getButtonText(), + button.getLocalBounds().withTrimmedLeft (juce::roundToInt (tickWidth) + 12).withTrimmedRight (2), + juce::Justification::centredLeft); + } + + void drawTickBox (juce::Graphics& g, juce::Component& /*component*/, float x, float y, float w, float h, const bool ticked, const bool isEnabled, const bool /*shouldDrawButtonAsHighlighted*/, const bool /*shouldDrawButtonAsDown*/) override + { + juce::Rectangle tickBounds (x, y, w, h); + + auto boxSize = tickBounds.getHeight() - 5; + + juce::Path p; + p.addTriangle (tickBounds.getX(), tickBounds.getY(), tickBounds.getX() + boxSize + 2, tickBounds.getY(), tickBounds.getX() + boxSize * 0.5f + 1, tickBounds.getY() + boxSize); + + auto tickColour = findColour (juce::ToggleButton::tickColourId); + g.setColour (isEnabled ? tickColour : tickColour.darker()); + + auto transform = juce::AffineTransform::rotation (!ticked ? juce::degreesToRadians (270.0f) : 0, + tickBounds.getCentreX(), + tickBounds.getCentreY()); + + if (!ticked) + transform = transform.translated (0, -boxSize * 0.25f + 1); + else + transform = transform.translated (-1, 3); + + g.fillPath (p, transform); + } + }; + ToggleButtonLnF toggleButtonLookAndFeel; + + juce::ToggleButton toggleButton; + juce::String name; + bool drawTopDivider; + Component::SafePointer content; + juce::SharedResourcePointer settings; + }; +} diff --git a/modules/melatonin_inspector/melatonin/components/color_picker.h b/modules/melatonin_inspector/melatonin/components/color_picker.h new file mode 100644 index 00000000..480090fe --- /dev/null +++ b/modules/melatonin_inspector/melatonin/components/color_picker.h @@ -0,0 +1,371 @@ +#pragma once + +#include "colour_property_component.h" +#include "juce_gui_extra/juce_gui_extra.h" +#include "melatonin_inspector/melatonin/components/overlay.h" +#include "preview.h" + +namespace melatonin +{ + class RGBAToggle : public juce::Component + { + public: + bool rgba = true; + std::function onClick; + + RGBAToggle() + { + rgba = settings->props->getBoolValue ("rgba", true); + } + + void paint (juce::Graphics& g) override + { + g.setColour (colors::customBackground); + g.fillRoundedRectangle (getLocalBounds().withSizeKeepingCentre (38, 16).toFloat(), 3); + g.setColour (colors::label); + g.setFont (juce::Font ("Verdana", 9, juce::Font::FontStyleFlags::bold)); + g.drawText (rgba ? "RGBA" : "HEX", getLocalBounds(), juce::Justification::centred); + } + + void mouseDown (const juce::MouseEvent& /*event*/) override + { + rgba = !rgba; + settings->props->setValue ("rgba", rgba); + if (onClick) + onClick(); + repaint(); + } + + private: + juce::SharedResourcePointer settings; + }; + + class ColorPicker : public juce::Component, + private ComponentModel::Listener, + private juce::ComponentListener, + private juce::FocusChangeListener + { + public: + std::function togglePickerCallback; + + explicit ColorPicker (ComponentModel& _model, Preview& p) : model (_model), preview (p) + { + addAndMakeVisible (colorPickerButton); + addAndMakeVisible (panel); + addAndMakeVisible (rgbaToggle); + + selectedColor = juce::Colours::transparentBlack; + + // we overlap the header, so let people click that as usual + setInterceptsMouseClicks (false, true); + + // needed for displaying colors + model.addListener (*this); + + // needed for preview resizes + preview.addComponentListener (this); + + // needed for color picking UX + juce::Desktop::getInstance().addFocusChangeListener (this); + + colorPickerButton.onClick = [this]() { + // uncertain why, but this must be accessed through "this" + togglePicker (this->colorPickerButton.on); + }; + + // update color properties with the correct display format + rgbaToggle.onClick = [this]() { componentModelChanged (model); }; + } + + ~ColorPicker() override + { + model.removeListener (*this); + preview.removeComponentListener (this); + juce::Desktop::getInstance().removeFocusChangeListener (this); + + if (root != nullptr && colorPickerButton.on) + root->removeMouseListener (this); + } + + void paint (juce::Graphics& g) override + { + if (colorPickerButton.on) + { + g.setColour (colors::black); + + // rect with only bottom corners rounded + g.fillRect (colorValueBounds.withBottom (4)); + g.fillRoundedRectangle (colorValueBounds.withTrimmedBottom (1).toFloat(), 4); + + g.setColour (colors::text); + g.setFont (juce::Font ("Verdana", 14.5, juce::Font::FontStyleFlags::plain)); + g.drawText (stringForColor (selectedColor), colorValueBounds.withTrimmedBottom (2), juce::Justification::centred); + } + + if (model.colors.empty()) + { + g.setColour (colors::propertyName); + g.setFont (juce::Font ("Verdana", 15, juce::Font::FontStyleFlags::plain)); + g.drawText ("No Color Properties", panelBounds.withTrimmedLeft (3).withTrimmedTop (2), juce::Justification::topLeft); + } + } + + void resized() override + { + auto buttonsArea = getLocalBounds().removeFromTop (32); + colorPickerButton.setBounds (buttonsArea.removeFromRight (32).translated (2, -4)); + buttonsArea.removeFromRight (12); + rgbaToggle.setBounds (buttonsArea.removeFromRight (38)); + + auto area = getLocalBounds(); + + // overlaps with the panel + bit of padding + colorValueBounds = area.removeFromTop (32).withTrimmedRight (36).withSizeKeepingCentre (rgbaToggle.rgba ? 100 : 90, 32); + + area.removeFromTop (5); + panelBounds = area; + if (!model.colors.empty()) + { + panel.setBounds (panelBounds); + } + } + + void mouseEnter (const juce::MouseEvent& event) override + { + if (root == nullptr) + return; + cursorToRestore = event.eventComponent->getMouseCursor(); + event.eventComponent->setMouseCursor (eyedropperCursor); + auto rootPos = event.getEventRelativeTo (root).getPosition(); + updatePicker (rootPos); + } + + void mouseMove (const juce::MouseEvent& event) override + { + if (root == nullptr) + return; + + // we see some interaction / glitching with the target app unless we always enforce this + // event.eventComponent->setMouseCursor (eyedropperCursor); + + auto rootPos = event.getEventRelativeTo (root).getPosition(); + updatePicker (rootPos); + } + + void mouseExit (const juce::MouseEvent& event) override + { + // always try to keep this cursor + event.eventComponent->setMouseCursor (cursorToRestore); + } + + void mouseDown (const juce::MouseEvent& event) override + { + if (root == nullptr) + return; + + if (mouseDownShouldOnlyFocus) + { + mouseDownShouldOnlyFocus = false; + return; + } + + if (colorPickerButton.on && selectedColor != juce::Colours::transparentBlack) + { + event.eventComponent->setMouseCursor (cursorToRestore); + model.pickedColor.setValue ((int) selectedColor.getARGB()); + model.refresh(); // update Last Picked + colorPickerButton.on = false; + colorPickerButton.onClick(); + repaint(); // needed in case we have nothing selected + } + } + + // when the preview size changes, update our snapshot size + void componentMovedOrResized (Component& component, bool, bool wasResized) override + { + if (&component == &preview && wasResized) + { + updateSnapshotDimensions(); + } + } + + // prevents an accidental color pick when clicking for focus reasons + // See Issue #67 + void globalFocusChanged (juce::Component* component) override + { + // when no component has focus, another app has focus + // and we're coming back to the target app / inspector + if (component == nullptr && colorPickerButton.on) + mouseDownShouldOnlyFocus = true; + } + + // called from the inspector component + void setRootComponent (Component* newRoot) + { + root = newRoot; + if (root == nullptr) + { + selectedColor = juce::Colours::transparentBlack; + reset(); + } + } + + void reset() + { + componentModelChanged (model); + } + + // close the picker if we are hidden + void visibilityChanged() override + { + if (!isVisible()) + { + colorPickerButton.on = false; + colorPickerButton.onClick(); + } + } + + private: + ComponentModel& model; + Preview& preview; + + juce::PropertyPanel panel { "Properties" }; + InspectorImageButton colorPickerButton { "eyedropper", { 0, 6 }, true }; + juce::Rectangle colorValueBounds; + juce::Rectangle panelBounds; + RGBAToggle rgbaToggle; + + juce::MouseCursor cursorToRestore = juce::MouseCursor::NormalCursor; + juce::Image eyedropperCursorImage = getIcon ("eyedropperon").rescaled (16, 16); + juce::MouseCursor eyedropperCursor { eyedropperCursorImage, 0, 15 }; + bool mouseDownShouldOnlyFocus = false; + + juce::Colour selectedColor { juce::Colours::transparentBlack }; + std::unique_ptr croppedSnapshot; + int snapshotRadiusWidth = 21; // defaults align with initial dimensions of preview + int snapshotRadiusHeight = 3; + + juce::Component* root {}; + + void togglePicker (bool on) + { + TRACE_COMPONENT(); + + if (on) + { + // get notified by all mouse activity in the target app/plugin + root->addMouseListener (this, true); + + reset(); + + preview.setVisible (true); + // pick an arbitrary first position in the overlay + if (root != nullptr) + updatePicker ({ root->getX() + 10, root->getY() + 10 }); + } + else + { + if (root != nullptr) + root->removeMouseListener (this); + + preview.switchToPreview(); + selectedColor = juce::Colours::transparentBlack; + } + + // might need to resize the panel if we need to toggle paint timings + // or there's a change in number of colors + getParentComponent()->resized(); + repaint(); + + // toggle overlay visibility + if (togglePickerCallback) + togglePickerCallback (!on); + } + + void updatePicker (juce::Point positionInRoot) + { + TRACE_COMPONENT(); + + if (!colorPickerButton.on) + return; + + updateSnapshot (positionInRoot); + auto snapshotBounds = croppedSnapshot->getBounds(); + selectedColor = croppedSnapshot->getPixelAt (snapshotBounds.getCentreX(), snapshotBounds.getCentreY()); + + // our snapshotted image will be larger than the preview panel (due to the bleed) + preview.setZoomedImage (*croppedSnapshot); + repaint(); + } + + // we continually update a cropped snapshot when picking + // this avoids a big cumbersome whole-plugin snapshot (bad for perf) + // but still lets users navigate the UI (changing tabs, popups, etc) + void updateSnapshot (juce::Point positionInRoot) + { + TRACE_COMPONENT(); + + if (root == nullptr) + return; + + auto snappedBounds = juce::Rectangle (positionInRoot.x - snapshotRadiusWidth, positionInRoot.y - snapshotRadiusHeight, snapshotRadiusWidth * 2 + 1, snapshotRadiusHeight * 2 + 1); + croppedSnapshot = std::make_unique (root->createComponentSnapshot (snappedBounds, false)); + } + + void updateSnapshotDimensions() + { + TRACE_COMPONENT(); + + // we are creating a 20x zoom + // (for example at the minimum width of 380, it's 19 pixels total) + int numberOfPixelsWidth = int (juce::jmax (380, preview.maxPreviewImageBounds.getWidth()) / preview.zoomScale); + int numberOfPixelsHeight = int (juce::jmax (100, preview.maxPreviewImageBounds.getHeight()) / preview.zoomScale); + + // remove 1 extra pixel to ensure odd number of pixels + if (numberOfPixelsWidth % 2 == 0) + numberOfPixelsWidth -= 1; + + if (numberOfPixelsHeight % 2 == 0) + numberOfPixelsHeight -= 1; + + // add 1 extra pixel for potential bleed on each side + int extraBleed = 2; + + // a width of 13 pixels results in 7 pixel radius + // a width of 14 pixels ALSO results in a 7 pixel radius + snapshotRadiusWidth = (numberOfPixelsWidth + extraBleed - 1) / 2; + snapshotRadiusHeight = (numberOfPixelsHeight + extraBleed - 1) / 2; + + // TODO: remove when preview height is adjustable + jassert (snapshotRadiusHeight == 3); + } + + void componentModelChanged (ComponentModel&) override + { + TRACE_COMPONENT(); + + panel.clear(); + juce::Array props; + + for (auto& nv : model.colors) + { + auto* prop = new ColourPropertyComponent (nv.value, colors::enumNameIfPresent (nv.name), rgbaToggle.rgba, true); + if (nv.name == "Last Picked") + { + prop->setColour (juce::PropertyComponent::labelTextColourId, colors::highlight); + } + prop->setLookAndFeel (&getLookAndFeel()); + props.add (prop); + } + panel.addProperties (props, 5); + resized(); + } + + juce::String stringForColor (juce::Colour& color) const + { + return rgbaToggle.rgba ? colors::rgbaString (color) : colors::hexString (color); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColorPicker) + }; +} diff --git a/modules/melatonin_inspector/melatonin/components/colour_property_component.h b/modules/melatonin_inspector/melatonin/components/colour_property_component.h new file mode 100644 index 00000000..38b521d3 --- /dev/null +++ b/modules/melatonin_inspector/melatonin/components/colour_property_component.h @@ -0,0 +1,139 @@ +#pragma once +#include "../helpers/colors.h" +namespace melatonin +{ + //==============================================================================*/ + class ColourPropertyComponent : public juce::PropertyComponent, + private juce::Value::Listener + { + public: + ColourPropertyComponent (const juce::Value& valueToControl, const juce::String& propertyName, bool displayRGBA = true, bool showAlpha = false) + : juce::PropertyComponent (propertyName), value (valueToControl), container (value, displayRGBA, showAlpha) + { + addAndMakeVisible (container); + value.addListener (this); + } + + juce::Value& getValueObject() + { + return value; + } + + void refresh() override + { + repaint(); + } + + void paint (juce::Graphics& g) override + { + PropertyComponent::paint (g); + + g.setColour (findColour (juce::BooleanPropertyComponent::backgroundColourId)); + g.fillRect (container.getBounds()); + + g.setColour (findColour (juce::BooleanPropertyComponent::outlineColourId)); + g.drawRect (container.getBounds()); + } + + private: + juce::Value value; + void valueChanged (juce::Value&) override + { + refresh(); + } + + class ColorSelector : public juce::ColourSelector, private juce::ChangeListener + { + public: + explicit ColorSelector (int selectorFlags = (showAlphaChannel | showColourAtTop | showSliders | showColourspace), + int _edgeGap = 4, + int _gapAroundColourSpaceComponent = 7) + : juce::ColourSelector (selectorFlags, _edgeGap, _gapAroundColourSpaceComponent) + { + addChangeListener (this); + } + + ~ColorSelector() override + { + if (onDismiss) + onDismiss(); + } + + void changeListenerCallback (juce::ChangeBroadcaster*) override + { + if (onChange) + onChange(); + } + + std::function onDismiss; + std::function onChange; + }; + + class Container : public Component + { + public: + Container (juce::Value& value_, bool rgba, bool a) + : value (value_), displayRGBA (rgba), alpha (a) + { + } + + void paint (juce::Graphics& g) override + { + auto c = juce::Colour ((uint32_t) int (value.getValue())); + + auto area = getLocalBounds(); + + g.setColour (c); + auto colorRect = area.removeFromLeft (20).withSizeKeepingCentre (18, 18).toFloat(); + g.fillRoundedRectangle (colorRect, 1.f); + + // help out the dark blue/purple/blacks + if (!colors::areContrasting (c, colors::panelBackgroundLighter, 0.1f)) + { + g.setColour (juce::Colours::grey.withAlpha (0.3f)); + g.drawRoundedRectangle (colorRect, 1.f, 1.f); + } + + area.removeFromLeft (8); + g.setColour (colors::propertyValue); + g.drawText (displayRGBA ? colors::rgbaString (c) : colors::hexString (c), area.withTrimmedBottom (1), juce::Justification::centredLeft); + } + + void mouseUp (const juce::MouseEvent& e) override + { + if (e.mouseWasClicked()) + { + auto selectorFlags = juce::ColourSelector::showSliders | juce::ColourSelector::showColourspace; + if (alpha) + selectorFlags |= juce::ColourSelector::showAlphaChannel; + + auto colourSelector = std::make_unique (selectorFlags); + + colourSelector->setLookAndFeel (&getLookAndFeel()); + colourSelector->setSize (300, 300); + colourSelector->setCurrentColour (juce::Colour ((uint32_t) int (value.getValue())), juce::dontSendNotification); + colourSelector->onDismiss = [this, parentRef = juce::WeakReference (this), cs = colourSelector.get()]() { + if (! parentRef.wasObjectDeleted()) + { + value = (int) cs->getCurrentColour().getARGB(); + repaint(); + } + }; + colourSelector->onChange = [this, cs = colourSelector.get()]() { + value = (int) cs->getCurrentColour().getARGB(); + repaint(); + }; + + auto& callout = juce::CallOutBox::launchAsynchronously (std::move (colourSelector), getScreenBounds().removeFromLeft (20), nullptr); + callout.setLookAndFeel (&getLookAndFeel()); + } + } + + juce::Value& value; + bool displayRGBA; + bool alpha; + }; + + Container container; + }; +} diff --git a/modules/melatonin_inspector/melatonin/components/component_tree_view_item.h b/modules/melatonin_inspector/melatonin/components/component_tree_view_item.h new file mode 100644 index 00000000..753834b8 --- /dev/null +++ b/modules/melatonin_inspector/melatonin/components/component_tree_view_item.h @@ -0,0 +1,339 @@ +#pragma once + +namespace melatonin +{ + class Overlay; + + class ComponentTreeViewItem + : public juce::TreeViewItem, + public juce::ComponentListener + { + public: + bool hasTabbedComponent = false; + + explicit ComponentTreeViewItem (juce::Component* c, + std::function outline, + std::function select) + : outlineComponentCallback (outline), selectComponentCallback (select), component (c) + { + // A few JUCE component types need massaging to get their child components + if (auto multiPanel = dynamic_cast (c)) + { + recursivelyAddChildrenFor (multiPanel->getCurrentTabbedComponent()); + } + else if (auto tabs = dynamic_cast (c)) + { + hasTabbedComponent = true; + for (int i = 0; i < tabs->getNumTabs(); ++i) + { + recursivelyAddChildrenFor (tabs->getTabContentComponent (i)); + } + } + else + { + addItemsForChildComponents(); + } + + setDrawsInLeftMargin (true); + + // Make our tree self-aware + component->addComponentListener (this); + } + + ~ComponentTreeViewItem() override + { + // The component can be deleted before this tree view item + if (component) + component->removeComponentListener (this); + } + + static juce::Path getKeyboardIcon() + { + static juce::Path p = [] { + static const unsigned char pathData[] = { 110, 109, 0, 0, 128, 66, 0, 0, 128, 66, 98, 154, 153, 229, 65, 0, 0, 128, 66, 0, 0, 0, 0, 102, 102, 185, 66, 0, 0, 0, 0, 0, 0, 0, 67, 108, 0, 0, 0, 0, 0, 0, 192, 67, 98, 0, 0, 0, 0, 102, 166, 209, 67, 154, 153, 229, 65, 0, 0, 224, 67, 0, 0, 128, 66, 0, 0, 224, 67, 108, 0, 0, 0, 68, 0, 0, 224, 67, 98, 51, 211, 8, 68, 0, 0, 224, 67, 0, 0, 16, 68, 102, 166, 209, 67, 0, 0, 16, 68, 0, 0, 192, 67, 108, 0, 0, 16, 68, 0, 0, 0, 67, 98, 0, 0, 16, 68, 102, 102, 185, 66, 51, 211, 8, 68, 0, 0, 128, 66, 0, 0, 0, 68, 0, 0, 128, 66, 108, 0, 0, 128, 66, 0, 0, 128, 66, 99, 109, 0, 0, 160, 66, 0, 0, 0, 67, 108, 0, 0, 224, 66, 0, 0, 0, 67, 98, 154, 153, 241, 66, 0, 0, 0, 67, 0, 0, 0, 67, 51, 51, 7, 67, 0, 0, 0, 67, 0, 0, 16, 67, 108, 0, 0, 0, 67, 0, 0, 48, 67, 98, 0, 0, 0, 67, 205, 204, 56, 67, 154, 153, 241, 66, 0, 0, 64, 67, 0, 0, 224, 66, 0, 0, 64, 67, 108, 0, 0, 160, 66, 0, 0, 64, 67, 98, 102, 102, 142, 66, 0, 0, 64, 67, 0, 0, 128, 66, 205, 204, 56, 67, 0, 0, 128, 66, 0, 0, 48, 67, 108, 0, 0, 128, 66, 0, 0, 16, 67, 98, 0, 0, 128, 66, 51, 51, 7, 67, 102, 102, 142, 66, 0, 0, 0, 67, 0, 0, 160, 66, 0, 0, 0, 67, 99, 109, 0, 0, 128, 66, 0, 0, 112, 67, 98, 0, 0, 128, 66, 51, 51, 103, 67, 102, 102, 142, 66, 0, 0, 96, 67, 0, 0, 160, 66, 0, 0, 96, 67, 108, 0, 0, 224, 66, 0, 0, 96, 67, 98, 154, 153, 241, 66, 0, 0, 96, 67, 0, 0, 0, 67, 51, 51, 103, 67, 0, 0, 0, 67, 0, 0, 112, 67, 108, 0, 0, 0, 67, 0, 0, 136, 67, 98, 0, 0, 0, 67, 102, 102, 140, 67, 154, 153, 241, 66, 0, 0, 144, 67, 0, 0, 224, 66, 0, 0, 144, 67, 108, 0, 0, 160, 66, 0, 0, 144, 67, 98, 102, 102, 142, 66, 0, 0, 144, 67, 0, 0, 128, 66, 102, 102, 140, 67, 0, 0, 128, 66, 0, 0, 136, 67, 108, 0, 0, 128, 66, 0, 0, 112, 67, 99, 109, 0, 0, 160, 66, 0, 0, 160, 67, 108, 0, 0, 224, 66, 0, 0, 160, 67, 98, 154, 153, 241, 66, 0, 0, 160, 67, 0, 0, 0, 67, 154, 153, 163, 67, 0, 0, 0, 67, 0, 0, 168, 67, 108, 0, 0, 0, 67, 0, 0, 184, 67, 98, 0, 0, 0, 67, 102, 102, 188, 67, 154, 153, 241, 66, 0, 0, 192, 67, 0, 0, 224, 66, 0, 0, 192, 67, 108, 0, 0, 160, 66, 0, 0, 192, 67, 98, 102, 102, 142, 66, 0, 0, 192, 67, 0, 0, 128, 66, 102, 102, 188, 67, 0, 0, 128, 66, 0, 0, 184, 67, 108, 0, 0, 128, 66, 0, 0, 168, 67, 98, 0, 0, 128, 66, 154, 153, 163, 67, 102, 102, 142, 66, 0, 0, 160, 67, 0, 0, 160, 66, 0, 0, 160, 67, 99, 109, 0, 0, 32, 67, 0, 0, 16, 67, 98, 0, 0, 32, 67, 51, 51, 7, 67, 51, 51, 39, 67, 0, 0, 0, 67, 0, 0, 48, 67, 0, 0, 0, 67, 108, 0, 0, 80, 67, 0, 0, 0, 67, 98, 205, 204, 88, 67, 0, 0, 0, 67, 0, 0, 96, 67, 51, 51, 7, 67, 0, 0, 96, 67, 0, 0, 16, 67, 108, 0, 0, 96, 67, 0, 0, 48, 67, 98, 0, 0, 96, 67, 205, 204, 56, 67, 205, 204, 88, 67, 0, 0, 64, 67, 0, 0, 80, 67, 0, 0, 64, 67, 108, 0, 0, 48, 67, 0, 0, 64, 67, 98, 51, 51, 39, 67, 0, 0, 64, 67, 0, 0, 32, 67, 205, 204, 56, 67, 0, 0, 32, 67, 0, 0, 48, 67, 108, 0, 0, 32, 67, 0, 0, 16, 67, 99, 109, 0, 0, 48, 67, 0, 0, 96, 67, 108, 0, 0, 80, 67, 0, 0, 96, 67, 98, 205, 204, 88, 67, 0, 0, 96, 67, 0, 0, 96, 67, 51, 51, 103, 67, 0, 0, 96, 67, 0, 0, 112, 67, 108, 0, 0, 96, 67, 0, 0, 136, 67, 98, 0, 0, 96, 67, 102, 102, 140, 67, 205, 204, 88, 67, 0, 0, 144, 67, 0, 0, 80, 67, 0, 0, 144, 67, 108, 0, 0, 48, 67, 0, 0, 144, 67, 98, 51, 51, 39, 67, 0, 0, 144, 67, 0, 0, 32, 67, 102, 102, 140, 67, 0, 0, 32, 67, 0, 0, 136, 67, 108, 0, 0, 32, 67, 0, 0, 112, 67, 98, 0, 0, 32, 67, 51, 51, 103, 67, 51, 51, 39, 67, 0, 0, 96, 67, 0, 0, 48, 67, 0, 0, 96, 67, 99, 109, 0, 0, 32, 67, 0, 0, 168, 67, 98, 0, 0, 32, 67, 154, 153, 163, 67, 51, 51, 39, 67, 0, 0, 160, 67, 0, 0, 48, 67, 0, 0, 160, 67, 108, 0, 0, 200, 67, 0, 0, 160, 67, 98, 102, 102, 204, 67, 0, 0, 160, 67, 0, 0, 208, 67, 154, 153, 163, 67, 0, 0, 208, 67, 0, 0, 168, 67, 108, 0, 0, 208, 67, 0, 0, 184, 67, 98, 0, 0, 208, 67, 102, 102, 188, 67, 102, 102, 204, 67, 0, 0, 192, 67, 0, 0, 200, 67, 0, 0, 192, 67, 108, 0, 0, 48, 67, 0, 0, 192, 67, 98, 51, 51, 39, 67, 0, 0, 192, 67, 0, 0, 32, 67, 102, 102, 188, 67, 0, 0, 32, 67, 0, 0, 184, 67, 108, 0, 0, 32, 67, 0, 0, 168, 67, 99, 109, 0, 0, 136, 67, 0, 0, 0, 67, 108, 0, 0, 152, 67, 0, 0, 0, 67, 98, 102, 102, 156, 67, 0, 0, 0, 67, 0, 0, 160, 67, 51, 51, 7, 67, 0, 0, 160, 67, 0, 0, 16, 67, 108, 0, 0, 160, 67, 0, 0, 48, 67, 98, 0, 0, 160, 67, 205, 204, 56, 67, 102, 102, 156, 67, 0, 0, 64, 67, 0, 0, 152, 67, 0, 0, 64, 67, 108, 0, 0, 136, 67, 0, 0, 64, 67, 98, 154, 153, 131, 67, 0, 0, 64, 67, 0, 0, 128, 67, 205, 204, 56, 67, 0, 0, 128, 67, 0, 0, 48, 67, 108, 0, 0, 128, 67, 0, 0, 16, 67, 98, 0, 0, 128, 67, 51, 51, 7, 67, 154, 153, 131, 67, 0, 0, 0, 67, 0, 0, 136, 67, 0, 0, 0, 67, 99, 109, 0, 0, 128, 67, 0, 0, 112, 67, 98, 0, 0, 128, 67, 51, 51, 103, 67, 154, 153, 131, 67, 0, 0, 96, 67, 0, 0, 136, 67, 0, 0, 96, 67, 108, 0, 0, 152, 67, 0, 0, 96, 67, 98, 102, 102, 156, 67, 0, 0, 96, 67, 0, 0, 160, 67, 51, 51, 103, 67, 0, 0, 160, 67, 0, 0, 112, 67, 108, 0, 0, 160, 67, 0, 0, 136, 67, 98, 0, 0, 160, 67, 102, 102, 140, 67, 102, 102, 156, 67, 0, 0, 144, 67, 0, 0, 152, 67, 0, 0, 144, 67, 108, 0, 0, 136, 67, 0, 0, 144, 67, 98, 154, 153, 131, 67, 0, 0, 144, 67, 0, 0, 128, 67, 102, 102, 140, 67, 0, 0, 128, 67, 0, 0, 136, 67, 108, 0, 0, 128, 67, 0, 0, 112, 67, 99, 109, 0, 0, 184, 67, 0, 0, 0, 67, 108, 0, 0, 200, 67, 0, 0, 0, 67, 98, 102, 102, 204, 67, 0, 0, 0, 67, 0, 0, 208, 67, 51, 51, 7, 67, 0, 0, 208, 67, 0, 0, 16, 67, 108, 0, 0, 208, 67, 0, 0, 48, 67, 98, 0, 0, 208, 67, 205, 204, 56, 67, 102, 102, 204, 67, 0, 0, 64, 67, 0, 0, 200, 67, 0, 0, 64, 67, 108, 0, 0, 184, 67, 0, 0, 64, 67, 98, 154, 153, 179, 67, 0, 0, 64, 67, 0, 0, 176, 67, 205, 204, 56, 67, 0, 0, 176, 67, 0, 0, 48, 67, 108, 0, 0, 176, 67, 0, 0, 16, 67, 98, 0, 0, 176, 67, 51, 51, 7, 67, 154, 153, 179, 67, 0, 0, 0, 67, 0, 0, 184, 67, 0, 0, 0, 67, 99, 109, 0, 0, 176, 67, 0, 0, 112, 67, 98, 0, 0, 176, 67, 51, 51, 103, 67, 154, 153, 179, 67, 0, 0, 96, 67, 0, 0, 184, 67, 0, 0, 96, 67, 108, 0, 0, 200, 67, 0, 0, 96, 67, 98, 102, 102, 204, 67, 0, 0, 96, 67, 0, 0, 208, 67, 51, 51, 103, 67, 0, 0, 208, 67, 0, 0, 112, 67, 108, 0, 0, 208, 67, 0, 0, 136, 67, 98, 0, 0, 208, 67, 102, 102, 140, 67, 102, 102, 204, 67, 0, 0, 144, 67, 0, 0, 200, 67, 0, 0, 144, 67, 108, 0, 0, 184, 67, 0, 0, 144, 67, 98, 154, 153, 179, 67, 0, 0, 144, 67, 0, 0, 176, 67, 102, 102, 140, 67, 0, 0, 176, 67, 0, 0, 136, 67, 108, 0, 0, 176, 67, 0, 0, 112, 67, 99, 109, 0, 0, 232, 67, 0, 0, 0, 67, 108, 0, 0, 248, 67, 0, 0, 0, 67, 98, 102, 102, 252, 67, 0, 0, 0, 67, 0, 0, 0, 68, 51, 51, 7, 67, 0, 0, 0, 68, 0, 0, 16, 67, 108, 0, 0, 0, 68, 0, 0, 48, 67, 98, 0, 0, 0, 68, 205, 204, 56, 67, 102, 102, 252, 67, 0, 0, 64, 67, 0, 0, 248, 67, 0, 0, 64, 67, 108, 0, 0, 232, 67, 0, 0, 64, 67, 98, 154, 153, 227, 67, 0, 0, 64, 67, 0, 0, 224, 67, 205, 204, 56, 67, 0, 0, 224, 67, 0, 0, 48, 67, 108, 0, 0, 224, 67, 0, 0, 16, 67, 98, 0, 0, 224, 67, 51, 51, 7, 67, 154, 153, 227, 67, 0, 0, 0, 67, 0, 0, 232, 67, 0, 0, 0, 67, 99, 109, 0, 0, 224, 67, 0, 0, 112, 67, 98, 0, 0, 224, 67, 51, 51, 103, 67, 154, 153, 227, 67, 0, 0, 96, 67, 0, 0, 232, 67, 0, 0, 96, 67, 108, 0, 0, 248, 67, 0, 0, 96, 67, 98, 102, 102, 252, 67, 0, 0, 96, 67, 0, 0, 0, 68, 51, 51, 103, 67, 0, 0, 0, 68, 0, 0, 112, 67, 108, 0, 0, 0, 68, 0, 0, 136, 67, 98, 0, 0, 0, 68, 102, 102, 140, 67, 102, 102, 252, 67, 0, 0, 144, 67, 0, 0, 248, 67, 0, 0, 144, 67, 108, 0, 0, 232, 67, 0, 0, 144, 67, 98, 154, 153, 227, 67, 0, 0, 144, 67, 0, 0, 224, 67, 102, 102, 140, 67, 0, 0, 224, 67, 0, 0, 136, 67, 108, 0, 0, 224, 67, 0, 0, 112, 67, 99, 109, 0, 0, 232, 67, 0, 0, 160, 67, 108, 0, 0, 248, 67, 0, 0, 160, 67, 98, 102, 102, 252, 67, 0, 0, 160, 67, 0, 0, 0, 68, 154, 153, 163, 67, 0, 0, 0, 68, 0, 0, 168, 67, 108, 0, 0, 0, 68, 0, 0, 184, 67, 98, 0, 0, 0, 68, 102, 102, 188, 67, 102, 102, 252, 67, 0, 0, 192, 67, 0, 0, 248, 67, 0, 0, 192, 67, 108, 0, 0, 232, 67, 0, 0, 192, 67, 98, 154, 153, 227, 67, 0, 0, 192, 67, 0, 0, 224, 67, 102, 102, 188, 67, 0, 0, 224, 67, 0, 0, 184, 67, 108, 0, 0, 224, 67, 0, 0, 168, 67, 98, 0, 0, 224, 67, 154, 153, 163, 67, 154, 153, 227, 67, 0, 0, 160, 67, 0, 0, 232, 67, 0, 0, 160, 67, 99, 101, 0, 0 }; + + juce::Path path; + path.loadPathFromData (pathData, sizeof (pathData)); + return path; + }(); + + return p; + } + + bool mightContainSubItems() override + { + return component != nullptr && (component->getNumChildComponents() > 0); + } + + // naive but functional... + void openTreeAndSelect (juce::Component* target) + { + TRACE_COMPONENT(); + + // don't let us select something already selected + if (component == target && !isSelected()) + { + forceSelectAndOpen (juce::dontSendNotification); + } + else if (component->isParentOf (target)) + { + jassert (target); + setOpen (true); + // recursively open up tree to get at target + for (int i = 0; i < getNumSubItems(); ++i) + { + dynamic_cast (getSubItem (i))->openTreeAndSelect (target); + } + } + } + + void paintItem (juce::Graphics& g, int w, int /*h*/) override + { + TRACE_COMPONENT(); + + if (!component) + return; + + // the root component is larger, so we want to clip it + // because we need to hack some padding in + auto itemArea = g.getClipBounds().removeFromBottom (28); + + if (isSelected()) + { + g.setColour (colors::black); + g.fillRect (itemArea); + } + + // we can't add padding to the viewport + // without screwing up the highlight style + // so we have to add to indent to make sure close/open still works + int textIndent = additionalTextIndent + 7; + + if (component->hasKeyboardFocus (false)) + { + auto iconBounds = juce::Rectangle (textIndent, itemArea.getY(), additionalTextIndent, itemArea.getHeight()).toFloat(); + + auto p = getKeyboardIcon(); + g.setColour (colors::highlight); + g.fillPath (p, p.getTransformToScaleToFit (iconBounds, true)); + textIndent += int (iconBounds.getWidth()) + 3; + } + + g.setColour (colors::text); + + if (isSelected()) + g.setColour (colors::treeItemTextSelected); + + if (!component->isVisible()) + g.setColour (colors::treeItemTextDisabled); + + auto name = componentString (component); + auto font = juce::Font ("Verdana", 15, juce::Font::FontStyleFlags::plain); + + g.setFont (font); + + g.drawText (name, textIndent, itemArea.getY(), w - textIndent, itemArea.getHeight(), juce::Justification::left, true); + } + + // must override to set the disclosure triangle color + void paintOpenCloseButton (juce::Graphics& g, const juce::Rectangle& area, juce::Colour /*backgroundColour*/, bool isMouseOver) override + { + // need to add 18px of indent here too since we can't add viewport padding + disclosureRect = area.translated (additionalTextIndent, 0); + getOwnerView()->getLookAndFeel().drawTreeviewPlusMinusBox (g, disclosureRect, colors::treeViewMinusPlusColor, isOpen(), isMouseOver); + } + + // yet another hack to make sure the disclosure triangle is properly clickable + // even though it's inset due to text indent + bool onlyTogglesDisclosure (const juce::MouseEvent& e) + { + // Convert the event back to its original form and then relative to our component + auto originalEvent = e.withNewPosition (e.position + getItemPosition (false).getPosition().toFloat()); + auto localEvent = originalEvent.getEventRelativeTo (getOwnerView()->getItemComponent (this)); + + if (mightContainSubItems() && localEvent.position.x < disclosureRect.getRight() + 7) + { + setOpen (!isOpen()); + return true; + } + + return false; + } + + // overriding this lets us decide when to select/note + // we modify the positioning of disclosure/item + // so we need manually handle to toggle disclosure without setting selected + // this is queried by TreeViewItem's setSelected + [[nodiscard]] bool canBeSelected() const override + { + return selectable; + } + + // by default we guard selection to be precise about hit boxes in the UI + void forceSelectAndOpen (juce::NotificationType notificationType = juce::sendNotification) + { + selectable = true; + setSelected (true, true, notificationType); + selectable = false; + setOpen (true); + } + + void itemClicked (const juce::MouseEvent& event) override + { + TRACE_COMPONENT(); + + if (onlyTogglesDisclosure (event)) + return; + + forceSelectAndOpen(); + selectComponentCallback (component); + selectTabbedComponentChildIfNeeded(); + if (mightContainSubItems()) + setOpen (true); + } + + void recursivelyCloseSubItems() + { + TRACE_COMPONENT ("name", getComponentName().toStdString()); + + for (int i = 0; i < getNumSubItems(); ++i) + { + // optimization: only recurse when item is open + if (getSubItem (i)->isOpen()) + { + getSubItem (i)->setOpen (false); + dynamic_cast (getSubItem (i))->recursivelyCloseSubItems(); + } + } + } + + [[nodiscard]] int countItemsRecursively() const noexcept + { + int total = 1; + + for (int i = 0; i < getNumSubItems(); ++i) + { + auto item = dynamic_cast (getSubItem (i)); + total += item->countItemsRecursively(); + } + + return total; + } + + void filterNodesRecursively (const juce::String& searchString) + { + TRACE_COMPONENT(); + + // Iterate over the child nodes of the current node + for (int i = getNumSubItems() - 1; i >= 0; --i) + { + // Recursively call the function on the current child node + if (auto* ct = dynamic_cast (getSubItem (i))) + { + ct->filterNodesRecursively (searchString); + } + } + // Check if the current node's name does not contain the search string + if (!getComponentName().containsIgnoreCase (searchString)) + { + // Remove the subtree rooted at the current node + if (getParentItem() != nullptr && getNumSubItems() == 0) + { + getParentItem()->removeSubItem (getIndexInParent()); + } + else + setOpen (true); + } + else if (getComponentName().startsWithIgnoreCase (searchString)) + { + outlineComponentCallback (component); + forceSelectAndOpen(); + setOpen (true); + } + else + { + setOpen (true); + } + } + + // Callback from the component listener. Reconstruct children when component is deleted + void componentChildrenChanged (juce::Component& /*changedComponent*/) override + { + validateSubItems(); + } + + void validateSubItems() + { + // Ideally we'd just re-render the sub-items branch: + // auto subItemToValidate = dynamic_cast (getSubItem (i)); + + // However, that wasn't working so the scorched earth strategy is + // if any child has a deleted component, we re-render the whole branch + // (we don't explicitly know if things were added or removed) + clearSubItems(); + addItemsForChildComponents(); + } + + juce::String getComponentName() + { + juce::String res = ""; + if (component && !component->getName().isEmpty()) + { + return component->getName(); + } + else if (component) + { + return type (*component); + } + return res; + } + + int getItemHeight() const override + { + auto normalItemHeight = 28; + + // root has top padding + return (getParentItem() == nullptr) ? normalItemHeight + 12 : normalItemHeight; + } + + std::function outlineComponentCallback; + std::function selectComponentCallback; + + private: + juce::Component::SafePointer component; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentTreeViewItem) + constexpr static int additionalTextIndent = 18; + bool selectable = false; + juce::Rectangle disclosureRect; + + void recursivelyAddChildrenFor (juce::Component* child) + { + // Components such as Labels can have a nullptr component child + // Rather than display empty placeholders in the tree view, we will hide them + if (child) + addSubItem (new ComponentTreeViewItem (child, outlineComponentCallback, selectComponentCallback)); + } + + void addItemsForChildComponents() + { + for (int i = 0; i < component->getNumChildComponents(); ++i) + { + auto child = component->getChildComponent (i); + if (componentString (child) != "Melatonin Overlay") + recursivelyAddChildrenFor (child); + } + } + + void selectTabbedComponentChildIfNeeded() + { + if (!getParentItem()) + return; + + auto parent = dynamic_cast (getParentItem()); + if (parent->hasTabbedComponent && !component->isVisible()) + { + dynamic_cast (parent->component.getComponent())->setCurrentTabIndex (getIndexInParent()); + } + } + }; +} diff --git a/modules/melatonin_inspector/melatonin/components/fps_meter.h b/modules/melatonin_inspector/melatonin/components/fps_meter.h new file mode 100644 index 00000000..f54eec95 --- /dev/null +++ b/modules/melatonin_inspector/melatonin/components/fps_meter.h @@ -0,0 +1,133 @@ +#pragma once +#include "juce_gui_basics/juce_gui_basics.h" +#include "melatonin_inspector/melatonin/helpers/colors.h" + +// VBlank was added in 7.0.3 +#if (JUCE_MAJOR_VERSION >= 7) && (JUCE_MINOR_VERSION >= 1 || JUCE_BUILDNUMBER >= 3) + #define MELATONIN_VBLANK 1 +#else + #define MELATONIN_VBLANK 0 +#endif + +namespace melatonin +{ + class FPSMeter : public juce::Component, private juce::Timer + { + public: + FPSMeter () + { + // don't repaint the parent + // unfortunately, on macOS, this no longer works + // See FAQ in README for more info + setOpaque (true); + + setInterceptsMouseClicks (false, false); + } + + void setRoot (juce::Component& o) + { + overlay = &o; + + overlay->addChildComponent (this); + } + + void clearRoot() + { + if (overlay) + overlay->removeChildComponent (this); + } + + void timerCallback() override + { + TRACE_EVENT ("component", "fps timer callback"); + repaint(); + } + + void visibilityChanged() override + { + frameTime = 0; + lastTime = 0; + fps = 0; + if (isVisible()) + { +#if MELATONIN_VBLANK + // right before every paint, call repaint + // syncs to ensure each paint call is preceded by a recalculation + vBlankCallback = { this, + [this] { + TRACE_EVENT ("component", "fps vBlankCallback"); + this->repaint(); + } }; +#else + // avoid as much aliasing with display refresh times as possible + // TODO: investigate optimal value + startTimerHz (120); +#endif + } + else + { +#if MELATONIN_VBLANK + vBlankCallback = {}; +#else + stopTimer(); +#endif + } + } + + void paint (juce::Graphics& g) override + { + TRACE_COMPONENT(); + + update(); + + // tried to go for pixel font but didn't work :/ + g.setImageResamplingQuality (juce::Graphics::ResamplingQuality::lowResamplingQuality); + g.setColour (colors::black); + g.fillRect (getLocalBounds()); + g.setColour (juce::Colours::green); + g.setFont (font); + g.drawText (juce::String (juce::String (fps) + " FPS"), getLocalBounds(), juce::Justification::centred, true); + } + + void update() + { + TRACE_COMPONENT(); + + auto now = juce::Time::getMillisecondCounterHiRes(); + auto elapsed = now - lastTime; + + if (juce::approximatelyEqual (lastTime, 0.0)) + { + lastTime = now; + return; + } + + if (juce::approximatelyEqual (frameTime, 0.0)) + { + // start without any smoothing + frameTime = elapsed; + } + else + { + // use a static number of hypothetical 30 fps to smooth the value + // https://stackoverflow.com/a/87333 + double smoothing = std::pow (0.9f, elapsed * 30 / 1000); + frameTime = (frameTime * smoothing) + (elapsed * (1.0f - smoothing)); + } + lastTime = now; + fps = juce::roundToInt (1000 / frameTime); + } + + private: + juce::Component* overlay = nullptr; + juce::Rectangle bounds; + juce::Font font = juce::Font (juce::Font::getDefaultMonospacedFontName(), 16.0f, juce::Font::plain); + double lastTime = juce::Time::getMillisecondCounterHiRes(); + double frameTime = 0; + int fps = 0; + +#if MELATONIN_VBLANK + juce::VBlankAttachment vBlankCallback; +#endif + }; +} diff --git a/modules/melatonin_inspector/melatonin/components/inspector_image_button.h b/modules/melatonin_inspector/melatonin/components/inspector_image_button.h new file mode 100644 index 00000000..0f62ed02 --- /dev/null +++ b/modules/melatonin_inspector/melatonin/components/inspector_image_button.h @@ -0,0 +1,57 @@ +#pragma once +#include "../helpers/misc.h" +#include + +namespace melatonin +{ + class InspectorImageButton : public juce::Component + { + public: + bool on = false; + std::function onClick; + std::function onDoubleClick; + + explicit InspectorImageButton (const juce::String& filename, juce::Point o = { 0, 0 }, bool toggleable = false) : offset (o) + { + setInterceptsMouseClicks (true, false); + setName (filename); + if (!toggleable) + imageOn = getIcon (filename); + else + { + // this is by convention + // the filenames should look like: icon-on.png / icon-off.png + imageOn = getIcon (filename + "on"); + imageOff = getIcon (filename + "off"); + } + } + + void paint (juce::Graphics& g) override + { + // Assumes exported at 2x + if (on || imageOff.isNull()) + g.drawImageTransformed (imageOn, juce::AffineTransform::scale (0.5f).translated (offset).translated ((float) offset.getX(), (float) offset.getY())); + else + g.drawImageTransformed (imageOff, juce::AffineTransform::scale (0.5f).translated (offset).translated ((float) offset.getX(), (float) offset.getY())); + } + + void mouseDown (const juce::MouseEvent& /*event*/) override + { + on = !on; + if (onClick != nullptr) + onClick(); + repaint(); + } + + void mouseDoubleClick (const juce::MouseEvent& /*event*/) override + { + if (onDoubleClick != nullptr) + onDoubleClick(); + } + + private: + juce::Image imageOn; + juce::Image imageOff; + juce::Point offset; + }; +} diff --git a/modules/melatonin_inspector/melatonin/components/overlay.h b/modules/melatonin_inspector/melatonin/components/overlay.h new file mode 100644 index 00000000..cc90e766 --- /dev/null +++ b/modules/melatonin_inspector/melatonin/components/overlay.h @@ -0,0 +1,580 @@ +#pragma once +#include "../helpers/misc.h" + +namespace melatonin +{ + + class Overlay : public juce::Component, public juce::ComponentListener + { + public: + Overlay() + { + setAlwaysOnTop (true); + setName ("Melatonin Overlay"); + // need to click on the resizeable corners of the component outlines + setInterceptsMouseClicks (false, true); + addAndMakeVisible (dimensions); + + juce::Label* labels[4] = { &distanceToTopHoveredLabel, &distanceToLeftHoveredLabel, &distanceToBottomHoveredLabel, &distanceToRightHoveredLabel }; + + for (auto* l : labels) + { + l->setFont (13.f); + l->setJustificationType (juce::Justification::centred); + l->setColour (juce::Label::textColourId, colors::white); + + addAndMakeVisible (l); + } + + dimensions.setFont (13.f); + dimensions.setJustificationType (juce::Justification::centred); + dimensions.setColour (juce::Label::textColourId, colors::white); + } + + ~Overlay() override + { + if (selectedComponent) + deselectComponent(); + } + + void paint (juce::Graphics& g) override + { + TRACE_COMPONENT(); + g.setColour (colors::overlayBoundingBox); + + // draws inwards as the line thickens + if (outlinedComponent) + g.drawRect (outlinedBounds, 2); + if (selectedComponent) + { + // Thinner border than hover (draws inwards) + g.drawRect (selectedBounds, 1); + + const float dashes[] { 2.0f, 2.0f }; + g.drawDashedLine (lineFromTopToParent, dashes, 2, 1.0f); + g.drawDashedLine (lineFromLeftToParent, dashes, 2, 1.0f); + + // corners outside + g.fillRect (juce::Rectangle (selectedBounds.getTopLeft().translated (-4, -4), selectedBounds.getTopLeft().translated (4, 4))); + g.fillRect (juce::Rectangle (selectedBounds.getTopRight().translated (-4, -4), selectedBounds.getTopRight().translated (4, 4))); + g.fillRect (juce::Rectangle (selectedBounds.getBottomRight().translated (-4, -4), selectedBounds.getBottomRight().translated (4, 4))); + g.fillRect (juce::Rectangle (selectedBounds.getBottomLeft().translated (-4, -4), selectedBounds.getBottomLeft().translated (4, 4))); + + // corners inside + g.setColour (colors::white); + g.fillRect (juce::Rectangle (selectedBounds.getTopLeft().translated (-3, -3), selectedBounds.getTopLeft().translated (3, 3))); + g.fillRect (juce::Rectangle (selectedBounds.getTopRight().translated (-3, -3), selectedBounds.getTopRight().translated (3, 3))); + g.fillRect (juce::Rectangle (selectedBounds.getBottomRight().translated (-3, -3), selectedBounds.getBottomRight().translated (3, 3))); + g.fillRect (juce::Rectangle (selectedBounds.getBottomLeft().translated (-3, -3), selectedBounds.getBottomLeft().translated (3, 3))); + + g.setColour (colors::overlayLabelBackground); + // text doesn't vertically center very nicely without manual offset + g.fillRoundedRectangle (dimensionsLabelBounds.withBottom (dimensionsLabelBounds.getBottom()).toFloat(), 2.0f); + } + + if (!hoveredBounds.isEmpty()) + { + g.setColour (colors::overlayDistanceToHovered); + g.drawRect (hoveredBounds.reduced (1)); + g.drawRect (selectedBounds.reduced (1)); + + const float dashes[] { 2.0f, 2.0f }; + g.drawLine (lineToTopHoveredComponent, 2); + g.drawLine (lineToLeftHoveredComponent, 2); + g.drawLine (lineToRightHoveredComponent, 2); + g.drawLine (lineToBottomHoveredComponent, 2); + + g.drawDashedLine (horConnectingLineToComponent, dashes, 2, 2.0f); + g.drawDashedLine (vertConnectingLineToComponent, dashes, 2, 2.0f); + + // text doesn't vertically center very nicely without manual offset + if (distanceToTopHoveredLabel.isVisible()) + g.fillRoundedRectangle (distanceToTopLabelBounds.withBottom (distanceToTopLabelBounds.getBottom()).toFloat(), 2.0f); + if (distanceToBottomHoveredLabel.isVisible()) + g.fillRoundedRectangle (distanceToBottomLabelBounds.withBottom (distanceToBottomLabelBounds.getBottom()).toFloat(), 2.0f); + if (distanceToLeftHoveredLabel.isVisible()) + g.fillRoundedRectangle (distanceToLeftLabelBounds.withBottom (distanceToLeftLabelBounds.getBottom()).toFloat(), 2.0f); + if (distanceToRightHoveredLabel.isVisible()) + g.fillRoundedRectangle (distanceToRightLabelBounds.withBottom (distanceToRightLabelBounds.getBottom()).toFloat(), 2.0f); + } + } + + void resized() override + { + TRACE_COMPONENT(); + if (outlinedComponent) + { + outlineComponent (outlinedComponent); + } + } + + // Components that belong to overlay are screened out by the caller (inspector) + void outlineComponent (Component* component) + { + TRACE_COMPONENT(); + + // get rid of another outline when re-entering a selected component + if (selectedComponent == component) + { + outlinedComponent = nullptr; + } + + outlinedComponent = component; + + if (outlinedComponent) + outlinedBounds = getLocalAreaForOutline (component); + + repaint(); + } + + void resetDistanceLinesToHovered() + { + lineToTopHoveredComponent = juce::Line(); + lineToLeftHoveredComponent = juce::Line(); + lineToRightHoveredComponent = juce::Line(); + lineToBottomHoveredComponent = juce::Line(); + + horConnectingLineToComponent = juce::Line(); + vertConnectingLineToComponent = juce::Line(); + + distanceToTopHoveredLabel.setVisible (false); + distanceToBottomHoveredLabel.setVisible (false); + distanceToLeftHoveredLabel.setVisible (false); + distanceToRightHoveredLabel.setVisible (false); + } + // draws a distance line when component is selected, showing distance to parent or hovered element + void outlineDistanceCallback (Component* hovComponent) + { + hoveredComponent = hovComponent; + if (hovComponent != selectedComponent && hovComponent != nullptr) + { + hoveredBounds = getLocalAreaForOutline (hovComponent); + calculateDistanceLinesToHovered(); + drawDistanceLabel(); + } + else + { + hoveredBounds = juce::Rectangle(); + + resetDistanceLinesToHovered(); + } + + repaint(); + } + + void selectComponent (Component* component) + { + TRACE_COMPONENT(); + + // allow us to clear selection + if (!component) + { + deselectComponent(); + return; + } + + if (selectedComponent) + { + bool isSameComponent = selectedComponent == component; + deselectComponent(); + if (isSameComponent) + return; + } + + // listen for those sweet resize calls + component->addComponentListener (this); + component->addMouseListener (this, false); + + // take over the outline from the hover + outlinedComponent = nullptr; + selectedComponent = component; + resizable = std::make_unique (component, &constrainer); + resizable->setBorderThickness (juce::BorderSize (6)); + addAndMakeVisible (*resizable); + setSelectedAndResizeableBounds (component); + repaint(); + + if (selectedComponent) + { + constrainer.setMinimumOnscreenAmounts (selectedComponent->getHeight(), selectedComponent->getWidth(), selectedComponent->getHeight(), selectedComponent->getWidth()); + // reset previous selection and update mouse cursor + selectedComponent->setMouseCursor (juce::MouseCursor::DraggingHandCursor); + } + } + + // When our selected component has been dragged or resized this is our callback + // We *must* then manually manage the size of the ResizableBorderComponent + void componentMovedOrResized (Component& component, bool wasMoved, bool wasResized) override + { + TRACE_COMPONENT(); + + if (wasResized || wasMoved) + { + // sort of annoying if hover triggers on resize + if (outlinedComponent) + outlinedComponent = nullptr; + setSelectedAndResizeableBounds (&component); + } + } + + void mouseExit (const juce::MouseEvent& /*event*/) override + { + outlinedComponent = nullptr; + if (!selectedComponent) + return; + + selectedComponent->setMouseCursor (juce::MouseCursor::NormalCursor); + repaint(); + } + + void mouseUp (const juce::MouseEvent& event) override + { + Component::mouseUp (event); + isDragging = false; + } + + void mouseEnter (const juce::MouseEvent&) override + { + if (!selectedComponent) + return; + + selectedComponent->setMouseCursor (juce::MouseCursor::DraggingHandCursor); + repaint(); + } + + void mouseMove (const juce::MouseEvent&) override + { + if (!selectedComponent) + return; + selectedComponent->setMouseCursor (juce::MouseCursor::DraggingHandCursor); + repaint(); + } + + void startDraggingComponent (const juce::MouseEvent& e) + { + // only allow dragging if the mouse is inside the selected component + if (selectedComponent && selectedComponent->getLocalBounds().contains (e.getEventRelativeTo (selectedComponent).getPosition())) + { + componentDragger.startDraggingComponent (selectedComponent, e); + isDragging = true; + } + } + + void dragSelectedComponent (const juce::MouseEvent& e) + { + // only allow dragging if the mouse is inside the selected component + bool isInside = selectedComponent && selectedComponent->getLocalBounds().contains (e.getEventRelativeTo (selectedComponent).getPosition()); + + if (isInside || (selectedComponent && isDragging)) + { + isDragging = true; + componentDragger.dragComponent (selectedComponent, e, &constrainer); + } + } + + private: + Component::SafePointer outlinedComponent; + Component::SafePointer hoveredComponent; + juce::Rectangle outlinedBounds; + + bool isDragging = false; + juce::ComponentDragger componentDragger; + juce::ComponentBoundsConstrainer boundsConstrainer; + + Component::SafePointer selectedComponent; + juce::Rectangle selectedBounds; + juce::Line lineFromTopToParent; + juce::Line lineFromLeftToParent; + + juce::Rectangle hoveredBounds; + juce::Line lineToTopHoveredComponent, + lineToLeftHoveredComponent, + lineToRightHoveredComponent, + lineToBottomHoveredComponent, + horConnectingLineToComponent, + vertConnectingLineToComponent; + + juce::Label distanceToTopHoveredLabel, + distanceToBottomHoveredLabel, + distanceToLeftHoveredLabel, + distanceToRightHoveredLabel; + juce::Rectangle distanceToTopLabelBounds, + distanceToLeftLabelBounds, + distanceToRightLabelBounds, + distanceToBottomLabelBounds; + + std::unique_ptr resizable; + juce::ComponentBoundsConstrainer constrainer; + + juce::Label dimensions; + juce::Rectangle dimensionsLabelBounds; + + juce::Rectangle getLocalAreaForOutline (Component* component, int borderSize = 2) + { + auto boundsPlusOutline = component->getBounds().expanded (borderSize); + return getLocalArea (component->getParentComponent(), boundsPlusOutline); + } + + void drawDimensionsLabel() + { + int labelWidth = (int) dimensions.getFont().getStringWidthFloat (dimensionsString (selectedBounds)) + 15; + int labelHeight = 15; + auto paddingToLabel = 4; + auto labelCenterX = selectedBounds.getX() + selectedBounds.getWidth() / 2; + + if ((selectedBounds.getBottom() + 20 + paddingToLabel) < getBottom()) + { + // label on bottom + dimensionsLabelBounds = juce::Rectangle ((int) (labelCenterX - labelWidth / 2), selectedBounds.getBottom() + paddingToLabel, labelWidth, labelHeight).expanded (2, 1); + } + else + { + // label on top + dimensionsLabelBounds = juce::Rectangle ((int) (labelCenterX - labelWidth / 2), selectedBounds.getY() - labelHeight - paddingToLabel, labelWidth, labelHeight).expanded (2, 1); + } + dimensions.setText (dimensionsString (selectedBounds), juce::dontSendNotification); + dimensions.setBounds (dimensionsLabelBounds); + dimensions.setVisible (true); + } + + void calculateLinesToParent() + { + // Todo(Investigate why this is happening) + if (selectedComponent == nullptr) + { + jassertfalse; + return; + } + + auto topOfComponent = selectedComponent->getBoundsInParent().getPosition().translated (selectedBounds.getWidth() / 2, -1); + auto leftOfComponent = selectedComponent->getBoundsInParent().getPosition().translated (-1, selectedBounds.getHeight() / 2); + + auto localTop = getLocalPoint (selectedComponent->getParentComponent(), topOfComponent); + auto localParentTop = getLocalPoint (selectedComponent->getParentComponent(), topOfComponent.withY (0)); + auto localLeft = getLocalPoint (selectedComponent->getParentComponent(), leftOfComponent); + auto localParentLeft = getLocalPoint (selectedComponent->getParentComponent(), leftOfComponent.withX (0)); + + lineFromTopToParent = juce::Line (localTop, localParentTop).toFloat(); + lineFromLeftToParent = juce::Line (localLeft, localParentLeft).toFloat(); + } + + void setSelectedAndResizeableBounds (Component* component) + { + selectedBounds = getLocalAreaForOutline (component, 1); + drawDimensionsLabel(); + calculateLinesToParent(); + resizable->setBounds (selectedBounds); + repaint(); + } + + void drawDistanceLabel() + { + // todo avoid overlapping of labels with dimensions label + if (selectedComponent && hoveredComponent) + { + int labelHeight = 15; + auto paddingToLabel = 4.0f; + + // top + if (lineToTopHoveredComponent.getLength() > 0) + { + int labelWidth = (int) distanceToTopHoveredLabel.getFont().getStringWidthFloat (distanceString (lineToTopHoveredComponent)) + 15; + + // todo draw on left or right side of line + auto labelCenterY = lineToTopHoveredComponent.getPointAlongLineProportionally (0.5f).getY(); + distanceToTopLabelBounds = juce::Rectangle ((int) (lineToTopHoveredComponent.getStartX() + paddingToLabel), + (int) labelCenterY - labelHeight / 2, + labelWidth, + labelHeight) + .expanded (2, 1); + distanceToTopHoveredLabel.setText (distanceString (lineToTopHoveredComponent), juce::dontSendNotification); + distanceToTopHoveredLabel.setBounds (distanceToTopLabelBounds); + } + + // bottom + if (lineToBottomHoveredComponent.getLength() > 0) + { + int labelWidth = (int) distanceToBottomHoveredLabel.getFont().getStringWidthFloat (distanceString (lineToBottomHoveredComponent)) + 15; + + // todo draw on left or right side of line + auto labelCenterY = lineToBottomHoveredComponent.getPointAlongLineProportionally (0.5f).getY(); + distanceToBottomLabelBounds = juce::Rectangle ((int) (lineToBottomHoveredComponent.getStartX() + paddingToLabel), + (int) labelCenterY - labelHeight / 2, + labelWidth, + labelHeight) + .expanded (2, 1); + distanceToBottomHoveredLabel.setText (distanceString (lineToBottomHoveredComponent), juce::dontSendNotification); + distanceToBottomHoveredLabel.setBounds (distanceToBottomLabelBounds); + } + + // right + if (lineToRightHoveredComponent.getLength() > 0) + { + int labelWidth = (int) distanceToRightHoveredLabel.getFont().getStringWidthFloat (distanceString (lineToRightHoveredComponent)) + 15; + + // todo draw on top or bottom side of line + auto labelCenterX = lineToRightHoveredComponent.getPointAlongLineProportionally (0.5f).getX(); + distanceToRightLabelBounds = juce::Rectangle ((int) labelCenterX - labelWidth / 2, + (int) (lineToRightHoveredComponent.getStartY() + paddingToLabel), + labelWidth, + labelHeight) + .expanded (2, 1); + distanceToRightHoveredLabel.setText (distanceString (lineToRightHoveredComponent), juce::dontSendNotification); + distanceToRightHoveredLabel.setBounds (distanceToRightLabelBounds); + } + + // left + if (lineToLeftHoveredComponent.getLength() > 0) + { + int labelWidth = (int) distanceToLeftHoveredLabel.getFont().getStringWidthFloat (distanceString (lineToLeftHoveredComponent)) + 15; + + // todo draw on top or bottom side of line + auto labelCenterX = lineToLeftHoveredComponent.getPointAlongLineProportionally (0.5f).getX(); + distanceToLeftLabelBounds = juce::Rectangle ((int) labelCenterX - labelWidth / 2, + (int) (lineToLeftHoveredComponent.getStartY() + paddingToLabel), + labelWidth, + labelHeight) + .expanded (2, 1); + distanceToLeftHoveredLabel.setText (distanceString (lineToLeftHoveredComponent), juce::dontSendNotification); + distanceToLeftHoveredLabel.setBounds (distanceToLeftLabelBounds); + } + } + + distanceToTopHoveredLabel.setVisible (lineToTopHoveredComponent.getLength() > 0); + distanceToBottomHoveredLabel.setVisible (lineToBottomHoveredComponent.getLength() > 0); + distanceToLeftHoveredLabel.setVisible (lineToLeftHoveredComponent.getLength() > 0); + distanceToRightHoveredLabel.setVisible (lineToRightHoveredComponent.getLength() > 0); + } + void calculateDistanceLinesToHovered() + { + lineToTopHoveredComponent = juce::Line(); + lineToLeftHoveredComponent = juce::Line(); + lineToRightHoveredComponent = juce::Line(); + lineToBottomHoveredComponent = juce::Line(); + + horConnectingLineToComponent = juce::Line(); + vertConnectingLineToComponent = juce::Line(); + + if (hoveredComponent && selectedComponent) + { + bool hovOnLeft = selectedBounds.getCentreX() > hoveredBounds.getCentreX(); + bool hovOnTop = selectedBounds.getCentreY() > hoveredBounds.getCentreY(); + + // if the hovered component is above the selected component + if (hovOnTop) + { + // if the hovered component is left of the selected component + if (hovOnLeft) + { + auto p1 = selectedBounds.getTopLeft().translated (0, selectedBounds.getHeight() / 2); + lineToRightHoveredComponent = juce::Line (p1, p1.withX (hoveredBounds.getRight())).toFloat(); + } + // if the hovered component is right of the selected component + else + { + auto p1 = selectedBounds.getTopRight().translated (0, selectedBounds.getHeight() / 2); + lineToLeftHoveredComponent = juce::Line (p1, p1.withX (hoveredBounds.getX())).toFloat(); + } + + auto p1 = selectedBounds.getTopLeft().translated (selectedBounds.getWidth() / 2, 0); + + // avoid drawing of top line if the hovered component is left or right of the selected component + // if(selectedBounds.getY() > hoveredBounds.getBottom() || selectedBounds.getBottom() < hoveredBounds.getY()) + lineToBottomHoveredComponent = juce::Line (p1, p1.withY (hoveredBounds.getBottom())).toFloat(); + + // avoid drawing horizontal line and if line is going into component + if (juce::approximatelyEqual (lineToBottomHoveredComponent.getStartY(), lineToBottomHoveredComponent.getEndY()) || lineToBottomHoveredComponent.getStartY() < lineToBottomHoveredComponent.getEndY()) + lineToBottomHoveredComponent = juce::Line(); + + // avoid drawing strictly vertical line lineToLeftHoveredComponent + if (juce::approximatelyEqual (lineToLeftHoveredComponent.getStartX(), lineToLeftHoveredComponent.getEndX()) || lineToLeftHoveredComponent.getStartX() > lineToLeftHoveredComponent.getEndX()) + lineToLeftHoveredComponent = juce::Line(); + + // avoid drawing strictly vertical line lineToRightHoveredComponent + if (juce::approximatelyEqual (lineToRightHoveredComponent.getStartX(), lineToRightHoveredComponent.getEndX()) || lineToRightHoveredComponent.getStartX() < lineToRightHoveredComponent.getEndX()) + lineToRightHoveredComponent = juce::Line(); + } + else + { + // if the hovered component is left of the selected component + if (hovOnLeft) + { + auto p1 = selectedBounds.getBottomLeft().translated (0, -selectedBounds.getHeight() / 2); + lineToRightHoveredComponent = juce::Line (p1, p1.withX (hoveredBounds.getRight())).toFloat(); + } + else + { + auto p1 = selectedBounds.getBottomRight().translated (0, -selectedBounds.getHeight() / 2); + lineToLeftHoveredComponent = juce::Line (p1, p1.withX (hoveredBounds.getX())).toFloat(); + } + + auto p1 = selectedBounds.getBottomLeft().translated (selectedBounds.getWidth() / 2, 0); + lineToTopHoveredComponent = juce::Line (p1, p1.withY (hoveredBounds.getY())).toFloat(); + + // avoid drawing horizontal line and if line is going into component + if (juce::approximatelyEqual (lineToTopHoveredComponent.getStartY(), lineToTopHoveredComponent.getEndY()) || lineToTopHoveredComponent.getStartY() > lineToTopHoveredComponent.getEndY()) + lineToTopHoveredComponent = juce::Line(); + + // avoid drawing strictly vertical line lineToLeftHoveredComponent + if (juce::approximatelyEqual (lineToLeftHoveredComponent.getStartX(), lineToLeftHoveredComponent.getEndX()) || lineToLeftHoveredComponent.getStartX() > lineToLeftHoveredComponent.getEndX()) + lineToLeftHoveredComponent = juce::Line(); + + // avoid drawing strictly vertical line lineToRightHoveredComponent + if (juce::approximatelyEqual (lineToRightHoveredComponent.getStartX(), lineToRightHoveredComponent.getEndX()) || lineToRightHoveredComponent.getStartX() < lineToRightHoveredComponent.getEndX()) + lineToRightHoveredComponent = juce::Line(); + } + + // adding missing lines to connect to hovered component + if (!hoveredBounds.contains (lineToRightHoveredComponent.getEnd().toInt().translated (-2, 0)) && lineToRightHoveredComponent.getLength() > 0) + { + juce::Point hoveredPoint; + if (hovOnTop) + { + hoveredPoint = hovOnLeft ? hoveredBounds.getBottomRight() : hoveredBounds.getBottomLeft(); + } + else + hoveredPoint = hovOnLeft ? hoveredBounds.getTopRight() : hoveredBounds.getTopLeft(); + vertConnectingLineToComponent = juce::Line (lineToRightHoveredComponent.getEnd().toFloat(), hoveredPoint.toFloat()); + } + if (!hoveredBounds.contains (lineToLeftHoveredComponent.getEnd().translated (2, 0).toInt()) && lineToLeftHoveredComponent.getLength() > 0) + { + juce::Point hoveredPoint; + if (hovOnTop) + { + hoveredPoint = hovOnLeft ? hoveredBounds.getBottomRight() : hoveredBounds.getBottomLeft(); + } + else + hoveredPoint = hovOnLeft ? hoveredBounds.getTopRight() : hoveredBounds.getTopLeft(); + vertConnectingLineToComponent = juce::Line (lineToLeftHoveredComponent.getEnd().toFloat(), hoveredPoint.toFloat()); + } + + if (!hoveredBounds.contains (lineToTopHoveredComponent.getEnd().toInt().translated (0, 2)) && lineToTopHoveredComponent.getLength() > 0) + { + juce::Point hoveredPoint = hovOnLeft ? hoveredBounds.getTopRight() : hoveredBounds.getTopLeft(); + horConnectingLineToComponent = juce::Line (lineToTopHoveredComponent.getEnd().toFloat(), hoveredPoint.toFloat()); + } + if (!hoveredBounds.contains (lineToBottomHoveredComponent.getEnd().toInt().translated (0, -2)) && lineToBottomHoveredComponent.getLength() > 0) + { + auto hoveredPoint = hovOnLeft ? hoveredBounds.getBottomRight() : hoveredBounds.getBottomLeft(); + horConnectingLineToComponent = juce::Line (lineToBottomHoveredComponent.getEnd().toFloat(), hoveredPoint.toFloat()); + } + } + } + + void deselectComponent() + { + dimensions.setVisible (false); + + if (selectedComponent != nullptr) + { + selectedComponent->removeComponentListener (this); + selectedComponent->removeMouseListener (this); + selectedComponent->setMouseCursor (juce::MouseCursor::NormalCursor); + } + + selectedComponent = nullptr; + repaint(); + } + }; + +} diff --git a/modules/melatonin_inspector/melatonin/components/preview.h b/modules/melatonin_inspector/melatonin/components/preview.h new file mode 100644 index 00000000..aedd7d0f --- /dev/null +++ b/modules/melatonin_inspector/melatonin/components/preview.h @@ -0,0 +1,315 @@ +#pragma once + +#include "melatonin_inspector/melatonin/component_model.h" + +namespace melatonin +{ + + class Preview : public juce::Component, public ComponentModel::Listener + { + public: + int zoomScale = 20; + juce::Rectangle maxPreviewImageBounds; + + explicit Preview (ComponentModel& _model) : model (_model) + { + setInterceptsMouseClicks (true, true); + model.addListener (*this); + addChildComponent (maxLabel); + addAndMakeVisible (timingToggle); + maxLabel.setColour (juce::Label::textColourId, colors::iconOff); + maxLabel.setJustificationType (juce::Justification::centredTop); + maxLabel.setFont (juce::Font ("Verdana", 18, juce::Font::FontStyleFlags::bold)); + + // by default timings aren't on + timingToggle.on = settings->props->getBoolValue ("showPerformanceTimings", false); + timingToggle.onClick = [this] { + // don't enable if we don't have timings + if (!model.hasPerformanceTiming()) + { + timingToggle.on = false; + return; + } + + settings->props->setValue ("showPerformanceTimings", timingToggle.on); + getParentComponent()->resized(); + }; + } + + ~Preview() override + { + model.removeListener (*this); + } + + void paint (juce::Graphics& g) override + { + TRACE_COMPONENT(); + + g.setColour (colors::black); + g.fillRect (contentBounds); + + if (showsPerformanceTimings()) + { + // background for the max section + maxLabel.setVisible (true); + g.setColour (colors::propertyValueError.withAlpha (0.17f)); + g.fillRoundedRectangle (maxBounds.toFloat(), 3); + + g.setFont (g.getCurrentFont().withHeight (15.0f)); + double exclusiveSum = (double) model.timing1.getValue() + (double) model.timing2.getValue() + (double) model.timing3.getValue(); + bool hasExclusive = exclusiveSum * 1000 * 1000 > 1; // at least 1 microsecond + bool hasChildren = model.hasChildren.getValue(); + + auto exclusive = exclusiveBounds; + g.setColour (hasExclusive ? colors::propertyName : colors::propertyValueDisabled); + g.drawText ("Exclusive", exclusive.removeFromLeft (100), juce::Justification::topLeft); + drawTimingText (g, exclusive.removeFromLeft (60), model.timing1.getValue(), !hasExclusive); + drawTimingText (g, exclusive.removeFromLeft (60), model.timing2.getValue(), !hasExclusive); + drawTimingText (g, exclusive.removeFromLeft (60), model.timing3.getValue(), !hasExclusive); + drawTimingText (g, exclusive.removeFromLeft (60), model.timingMax.getValue(), !hasExclusive); + + auto withChildren = withChildrenBounds; + g.setColour (hasChildren ? colors::propertyName : colors::propertyValueDisabled); + g.drawText ("With Children", withChildren.removeFromLeft (100), juce::Justification::topLeft); + drawTimingText (g, withChildren.removeFromLeft (60), model.timingWithChildren1, !hasChildren); + drawTimingText (g, withChildren.removeFromLeft (60), model.timingWithChildren2, !hasChildren); + drawTimingText (g, withChildren.removeFromLeft (60), model.timingWithChildren3, !hasChildren); + drawTimingText (g, withChildren.removeFromLeft (60), model.timingWithChildrenMax, !hasChildren); + } + else + { + maxLabel.setVisible (false); + } + + if (colorPicking) + { + // lets see them pixels! + g.saveState(); + g.setImageResamplingQuality (juce::Graphics::ResamplingQuality::lowResamplingQuality); + + /* the zoomed snapshot is always *larger* than our preview area + + bleed + │ + ┌▼┌────────────────────┬─┐ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + └─┴────────────────────┴─┘ + */ + int imageY = contentBounds.getY(); + int bleedPerSide = (previewImage.getWidth() * zoomScale - getWidth()) / 2; + g.drawImageTransformed (previewImage, juce::AffineTransform::scale ((float) zoomScale, (float) zoomScale).translated ((float) -bleedPerSide, (float) imageY)); + + // draw grid + g.setColour (juce::Colours::grey.withAlpha (0.3f)); + for (auto i = 0; i < contentBounds.getHeight() / zoomScale; i++) + g.drawHorizontalLine (imageY + i * zoomScale, 0, (float) getWidth()); + + int numberOfVerticalLines = previewImage.getWidth() - 1; + auto inset = zoomScale - bleedPerSide; + for (auto i = 0; i < numberOfVerticalLines; i++) + g.drawVerticalLine (inset + i * zoomScale, (float) imageY, (float) contentBounds.getBottom()); + + // highlight the center pixel in first black, then white boxes + g.setColour (juce::Colours::black); + + // grab the top left of the center pixel + int highlightedPixelX = inset + (numberOfVerticalLines - 1) / 2 * zoomScale; + int highlightY = (int) imageY + contentBounds.getHeight() / 2; + g.drawRect (highlightedPixelX, highlightY - 10, zoomScale, zoomScale); + g.setColour (juce::Colours::white); + g.drawRect (highlightedPixelX - 2, highlightY - 12, 24, 24, 2); + g.restoreState(); // back to full quality drawing + } + else if (!previewImage.isNull()) + { + // TODO: odd this is needed (otherwise there's alpha in the state from somewhere) + g.setOpacity (1.0f); + + // don't want our checkers aliased + // so lets draw exact pixels + g.saveState(); + g.setImageResamplingQuality (juce::Graphics::ResamplingQuality::lowResamplingQuality); + + // fits and scale the preview image and while doing so, grab the transform + // this lets us reuse the position/scaling for clipping the transparency grid + auto transform = juce::RectanglePlacement (juce::RectanglePlacement::centred).getTransformToFit (previewImage.getBounds().toFloat(), maxPreviewImageBounds.toFloat()); + auto resizedPreviewImageBounds = previewImage.getBounds().transformedBy (transform); + + // the transform is relative to maxPreviewImageBounds and has an "offset" already + // For example, the Y offset will be at 48px from the Preview component + // this would clip too much of the checkerboard (which is already placed at that offset). + // An alternative solution here would be to have the checkerboard have the same size as Preview + // but only start drawing the checkers at maxPreviewImageBounds + auto checkersClipBounds = resizedPreviewImageBounds.translated(-maxPreviewImageBounds.getX(), -maxPreviewImageBounds.getY()); + + // clipping keeps the checkerboard background fixed across image positions / sizes + auto clippedCheckers = checkerboard.getClippedImage (checkersClipBounds); + g.drawImage (clippedCheckers, resizedPreviewImageBounds.toFloat(), juce::RectanglePlacement::doNotResize); + + // back to drawing hi-res for the image + g.restoreState(); + g.drawImageTransformed (previewImage, transform); + } + } + + void resized() override + { + TRACE_COMPONENT(); + + auto area = getLocalBounds(); + buttonsBounds = area.removeFromTop (32); + timingToggle.setBounds (buttonsBounds.removeFromRight (32)); + buttonsBounds.removeFromRight (12); + contentBounds = area; + + if (showsPerformanceTimings()) + { + auto performanceBounds = area.removeFromBottom (50).withLeft (32); + maxBounds = performanceBounds.withLeft (304).withWidth (80).translated (0, -4).withTrimmedBottom (4); + auto pivot = maxBounds.getTopRight().toFloat(); + exclusiveBounds = performanceBounds.removeFromTop (25); + withChildrenBounds = performanceBounds; + maxLabel.setBounds (maxBounds.withLeft ((int) pivot.getX() - 50)); + maxLabel.setTransform (juce::AffineTransform().rotated (-juce::MathConstants::halfPi, pivot.getX(), pivot.getY()).translated (-22, -2)); + } + else + { + exclusiveBounds = juce::Rectangle(); + withChildrenBounds = juce::Rectangle(); + } + + // default for this ends up being 32 48 382 68 + maxPreviewImageBounds = area.reduced (32, 16); + drawCheckerboard(); + } + + void mouseDoubleClick (const juce::MouseEvent&) override + { + if (model.getSelectedComponent()) + { + // clear timings + // TODO: these should be settable from model + auto props = model.getSelectedComponent()->getProperties(); + if (model.hasPerformanceTiming()) + { + juce::StringArray items = { "timing1", "timing2", "timing3", "timingMax", "timingWithChildren1", "timingWithChildren2", "timingWithChildren3", "timingWithChildrenMax" }; + for (auto& item : items) + props.set (item, 0.0); + model.refresh(); + } + + // force repaint to grab new timings + model.getSelectedComponent()->repaint(); + + // update the UI + repaint(); + } + } + + // called by color picker + void setZoomedImage (const juce::Image& image) + { + previewImage = image; + colorPicking = true; + repaint(); + } + + void switchToPreview() + { + colorPicking = false; + componentModelChanged (model); + repaint(); + } + + [[nodiscard]] bool showsPerformanceTimings() + { + return !colorPicking && model.hasPerformanceTiming() && timingToggle.on; + } + + private: + juce::Image previewImage; + juce::Image checkerboard; + juce::SharedResourcePointer settings; + ComponentModel& model; + bool colorPicking = false; + + juce::Rectangle buttonsBounds; + juce::Rectangle contentBounds; + juce::Rectangle exclusiveBounds; + juce::Rectangle withChildrenBounds; + juce::Rectangle maxBounds; + + InspectorImageButton timingToggle { "timing", { 4, 4 }, true }; + juce::Label maxLabel { "max", "MAX" }; + + void componentModelChanged (ComponentModel&) override + { + TRACE_COMPONENT(); + + if (auto component = model.getSelectedComponent()) + previewImage = component->createComponentSnapshot ({ component->getWidth(), component->getHeight() }, false, 2.0f); + else + previewImage = juce::Image(); + + colorPicking = false; + } + + static void drawTimingText (juce::Graphics& g, juce::Rectangle bounds, double value, bool disabled = false) + { + auto text = timingWithUnits (disabled ? 0 : value); + + auto ms = value * 1000; + if (disabled || ms * 1000 < 1) + g.setColour (colors::propertyValueDisabled); + else if (ms > 3) + g.setColour (colors::propertyValueWarn); + else if (ms > 8) + g.setColour (colors::propertyValueError); + else + g.setColour (colors::propertyValue); + + g.drawText (text, bounds, juce::Justification::topLeft); + } + + static juce::String timingWithUnits (double value) + { + double ms = value * 1000; + if (ms * 1000 < 1) + return "-"; + else if (ms < 1) + return juce::String (ms * 1000, 1).dropLastCharacters (2) + juce::String (juce::CharPointer_UTF8 ("\xc2\xb5")) + "s"; // µs + else + return juce::String (ms, 1) + "ms"; + } + + // we draw the checkerboard at the full preview width and cache it + // it's later clipped as needed + void drawCheckerboard() + { + TRACE_COMPONENT(); + + if (maxPreviewImageBounds.isEmpty()) + return; + + checkerboard = { juce::Image::RGB, maxPreviewImageBounds.getWidth(), maxPreviewImageBounds.getHeight(), true }; + juce::Graphics g2 (checkerboard); + int checkerSize = settings->props->getIntValue ("checkerSize", 4); + + for (int i = 0; i < maxPreviewImageBounds.getWidth(); i += checkerSize) + { + // keeps checkerboard background consistent across image positions / sizes + // allows for initial or ending partial checker + for (auto j = 0; j < maxPreviewImageBounds.getHeight(); j += checkerSize) + { + g2.setColour (((i + j) / checkerSize) % 2 == 0 ? colors::checkerLight : colors::checkerDark); + g2.fillRect (i, j, checkerSize, checkerSize); + } + } + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Preview) + }; +} diff --git a/modules/melatonin_inspector/melatonin/components/properties.h b/modules/melatonin_inspector/melatonin/components/properties.h new file mode 100644 index 00000000..16c128cb --- /dev/null +++ b/modules/melatonin_inspector/melatonin/components/properties.h @@ -0,0 +1,137 @@ +#pragma once +#include "melatonin_inspector/melatonin/component_model.h" + +namespace melatonin +{ + class Properties : public juce::Component, private ComponentModel::Listener + { + public: + // Used internally by the inspector and shouldn't be redundantly displayed + static inline juce::StringArray propertiesToIgnore { "paddingLeft", + "paddingRight", + "paddingTop", + "paddingBottom", + "timing1", + "timing2", + "timing3", + "timingMax" }; + + explicit Properties (ComponentModel& _model) : model (_model) + { + reset(); + + addAndMakeVisible (&panel); + model.addListener (*this); + } + + ~Properties() override + { + model.removeListener (*this); + } + + void resized() override + { + TRACE_COMPONENT(); + // let the property panel know what total height we need to be + panel.setBounds (getLocalBounds().withTrimmedTop (padding)); + } + + void reset() + { + updateProperties(); + resized(); + } + + private: + ComponentModel& model; + juce::PropertyPanel panel { "Properties" }; + + int padding = 3; + + void componentModelChanged (ComponentModel&) override + { + updateProperties(); + } + + void updateProperties() + { + TRACE_COMPONENT(); + panel.clear(); + + if (!model.getSelectedComponent()) + return; + + auto props = createPropertyComponents(); + for (auto* p : props) + { + p->setLookAndFeel (&getLookAndFeel()); + } + panel.addProperties (props, padding); + + resized(); + } + + [[nodiscard]] juce::Array createPropertyComponents() const + { + TRACE_COMPONENT(); + + // we can't actually set these values from the front end, so disable them + auto hasCachedImage = new juce::BooleanPropertyComponent (model.hasCachedImageValue, "CachedToImage", ""); + hasCachedImage->setEnabled (false); + + // Always have class up top + juce::Array props = { + new juce::TextPropertyComponent (model.typeValue, "Class", 200, false, false), + new juce::TextPropertyComponent (model.nameValue, "Name", 200, false, false), + }; + + // Then prioritize model properties + for (auto& nv : model.namedProperties) + { + if (nv.value.getValue().isBool()) + props.add (new juce::BooleanPropertyComponent (nv.value, nv.name, "")); + else if (nv.value.getValue().isInt64() && nv.name.getLastCharacters (2) == "At") + { + auto datetime = juce::Value (juce::Time (nv.value.getValue()).toString (false, true, true, true)); + auto datetimeProp = new juce::TextPropertyComponent (datetime, nv.name, 200, false, false); + datetimeProp->setEnabled (false); + props.add (datetimeProp); + } + else if (!propertiesToIgnore.contains (nv.name)) + { + auto customProperty = new juce::TextPropertyComponent (nv.value, nv.name, 200, false); + customProperty->getProperties().set ("isUserProperty", true); + props.add (customProperty); + } + } + + // add class specific properies + if (dynamic_cast (model.getSelectedComponent())) + { + props.addArray (juce::Array { + new juce::BooleanPropertyComponent (model.isToggleable, "Is Toggleable", ""), + new juce::BooleanPropertyComponent (model.toggleState, "Toggle State", ""), + new juce::BooleanPropertyComponent (model.clickTogglesState, "Clicking Toggles State", ""), + new juce::TextPropertyComponent (model.radioGroupId, "Radio Group ID", 5, false) }); + } + + // then the rest of the component flags + props.addArray (juce::Array { + new juce::TextPropertyComponent (model.lookAndFeelValue, "LookAndFeel", 200, false, false), + new juce::BooleanPropertyComponent (model.visibleValue, "Visible", ""), + new juce::BooleanPropertyComponent (model.enabledValue, "Enabled", ""), + new juce::TextPropertyComponent (model.alphaValue, "Alpha", 5, false), + new juce::BooleanPropertyComponent (model.opaqueValue, "Opaque", ""), + new juce::TextPropertyComponent (model.fontValue, "Font", 5, false, false), + new juce::BooleanPropertyComponent (model.wantsFocusValue, "Wants Keyboard Focus", ""), + new juce::BooleanPropertyComponent (model.accessibilityHandledValue, "Accessibility", ""), + hasCachedImage, + new juce::BooleanPropertyComponent (model.interceptsMouseValue, "Intercepts Mouse", ""), + new juce::BooleanPropertyComponent (model.childrenInterceptsMouseValue, "Children Intercepts", "") }); + + return props; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Properties) + }; +} diff --git a/modules/melatonin_inspector/melatonin/components/tree_component.h b/modules/melatonin_inspector/melatonin/components/tree_component.h new file mode 100644 index 00000000..6148c27a --- /dev/null +++ b/modules/melatonin_inspector/melatonin/components/tree_component.h @@ -0,0 +1,61 @@ +#pragma once + +#include "component_tree_view_item.h" +#include "melatonin_inspector/melatonin/component_model.h" + +namespace melatonin +{ + class TreeComponent : public juce::Component, private ComponentModel::Listener, private juce::FocusChangeListener + { + public: + + TreeComponent (ComponentModel& _model): model(_model) + { + juce::Desktop::getInstance ().addFocusListener (this); + model.addListener (*this); + } + + ~TreeComponent() override + { + juce::Desktop::getInstance ().removeFocusListener (this); + model.removeListener (*this); + } + + void globalFocusChanged (juce::Component*) override + { + repaint(); + } + + void resized() override + { + auto area = getLocalBounds(); + + //using btn toggle state (better to switch to using class variable + //or inspectors prop) + + if (tree.isVisible()) + { + auto searchRow = area.removeFromTop (30).reduced (4, 4); + clearBtn.setBounds (searchRow.removeFromRight (56)); + searchRow.removeFromRight (8); + searchBox.setBounds (searchRow); + + tree.setBounds (area); // padding in these default components are a mess + emptySearchLabel.setBounds (area.reduced (4)); + } + else + emptySelectionPrompt.setBounds (area); + } + + private: + ComponentModel& model; + + juce::TreeView tree; + juce::Label emptySelectionPrompt { "SelectionPrompt", "Select any component to see components tree" }; + juce::Label emptySearchLabel { "EmptySearchResultsPrompt", "No component found" }; + juce::TextEditor searchBox { "Search box" }; + juce::TextButton clearBtn { "clear" }; + std::unique_ptr rootItem; + + }; +} diff --git a/modules/melatonin_inspector/melatonin/helpers/colors.h b/modules/melatonin_inspector/melatonin/helpers/colors.h new file mode 100644 index 00000000..44a11531 --- /dev/null +++ b/modules/melatonin_inspector/melatonin/helpers/colors.h @@ -0,0 +1,99 @@ +#pragma once +#include "colour_ids.h" +#include "juce_gui_basics/juce_gui_basics.h" + +namespace melatonin::colors +{ + const juce::Colour white = juce::Colours::white; + const juce::Colour black = juce::Colour::fromRGB (0, 0, 0); + + // background & panel + const juce::Colour background = juce::Colour::fromRGB (43, 29, 50); + const juce::Colour headerBackground = juce::Colour::fromRGB (28, 14, 34); + const juce::Colour panelBackgroundLighter = juce::Colour::fromRGB (45, 22, 55); + const juce::Colour panelBackgroundDarker = juce::Colour::fromRGB (38, 18, 52); + const juce::Colour panelLineSeparator = juce::Colour::fromRGB (12, 7, 19); + const juce::Colour disclosure = juce::Colour::fromRGB (154, 190, 255); + const juce::Colour scrollbar = juce::Colour::fromRGB (154, 190, 255); + const juce::Colour scrollbarBackground = juce::Colour::fromRGB (12, 17, 9); + const juce::Colour highlight = juce::Colour::fromRGB (255, 229, 138); + + // text + const juce::Colour text = juce::Colour::fromRGB (118, 143, 190); + const juce::Colour label = juce::Colour::fromRGB (131, 146, 175); + const juce::Colour highlightedText = juce::Colour::fromRGB (55, 55, 55); + const juce::Colour caret = juce::Colour::fromRGB (20, 157, 249); + const juce::Colour searchText = juce::Colour::fromRGB (92, 82, 111); + + // tree + const juce::Colour treeBackgroundLighter = juce::Colour::fromRGB (34, 17, 46); + const juce::Colour treeBackgroundDarker = juce::Colour::fromRGB (24, 10, 35); + const juce::Colour treeItemTextSelected = juce::Colour::fromRGB (206, 234, 255); + const juce::Colour treeItemTextDisabled = juce::Colour::fromRGBA (119, 111, 129, 165); + const juce::Colour treeViewMinusPlusColor = juce::Colour::fromRGB (119, 111, 129); + + // props + const juce::Colour propertyName = text; + const juce::Colour propertyValue = juce::Colour::fromRGB (206, 235, 255); + const juce::Colour checkboxCheck = juce::Colour::fromRGB (41, 11, 79); + const juce::Colour customBackground = juce::Colour::fromRGB (25, 11, 36).darker(); + const juce::Colour propertyValueWarn = juce::Colour::fromRGB (255, 117, 117); + const juce::Colour propertyValueError = juce::Colour::fromRGB (255, 51, 51); + const juce::Colour propertyValueDisabled = treeItemTextDisabled; + const juce::Colour iconOff = juce::Colour::fromRGB (95, 82, 111); + + // overlay + const juce::Colour overlayBoundingBox = juce::Colour::fromRGB (66, 157, 226); + const juce::Colour overlayLabelBackground = juce::Colour::fromRGB (20, 157, 249); + const juce::Colour boxModelBoundingBox = juce::Colour::fromRGB (66, 157, 226); + const juce::Colour overlayDistanceToHovered = juce::Colour::fromRGB (212, 86, 63); + + const juce::Colour checkerDark = juce::Colour::fromRGB (51, 51, 51); + const juce::Colour checkerLight = juce::Colour::fromRGB (104, 104, 104); + + static inline juce::String rgbaString (juce::Colour& color) + { + if (color.getAlpha() < 255) + return juce::String::formatted ("%d, %d, %d, %d", + color.getRed(), + color.getGreen(), + color.getBlue(), + color.getAlpha()); + else + return juce::String::formatted ("%d, %d, %d", + color.getRed(), + color.getGreen(), + color.getBlue()); + } + + static inline juce::String hexString (juce::Colour& color) + { + return color.toDisplayString (true); + } + + // juce::ColourHelpers is an internal implementation, this is copied + static inline bool areContrasting (const juce::Colour& c1, const juce::Colour& c2, float minContrast = 0.5f) + { + auto c1Luma = 0.2999f * c1.getFloatRed() + 0.5870f * c1.getFloatGreen() + 0.1140f * c1.getFloatBlue(); + auto c2Luma = 0.2999f * c2.getFloatRed() + 0.5870f * c2.getFloatGreen() + 0.1140f * c2.getFloatBlue(); + + if (std::abs (c1Luma - c2Luma) >= minContrast) + return true; + + return false; + } + + static inline juce::String enumNameIfPresent (juce::String idString) + { + if (!idString.startsWith ("jcclr_")) + return idString; // or handle the error as needed + + int colourId = idString.substring (6).getHexValue32(); + + for (const auto& mapping : colourIdNames) + if (mapping.value == colourId) + return mapping.name; + + return idString; + } +} diff --git a/modules/melatonin_inspector/melatonin/helpers/colour_ids.h b/modules/melatonin_inspector/melatonin/helpers/colour_ids.h new file mode 100644 index 00000000..9dc2cc35 --- /dev/null +++ b/modules/melatonin_inspector/melatonin/helpers/colour_ids.h @@ -0,0 +1,132 @@ +// Generated by update_juce_colours.rb on 2023-08-12 +// This file collects ColourIds from the JUCE GUI module by parsing the C++ files. +#pragma once +#include + +namespace melatonin::colors +{ + + struct ColourIdMapping + { + int value; + const char* name; + }; + + constexpr ColourIdMapping colourIdNames[] = + { + { 0x1004010, "textColourId" }, // juce_DrawableButton.h:158 The colour to use for the button's text label. + { 0x1004013, "textColourOnId" }, // juce_DrawableButton.h:159 The colour to use for the button's text when the button's toggle state is "on". + { 0x1004011, "backgroundColourId" }, // juce_DrawableButton.h:161 The colour used to fill the button's background (when + { 0x1004012, "backgroundOnColourId" }, // juce_DrawableButton.h:165 The colour used to fill the button's background (when + { 0x1001f00, "textColourId" }, // juce_HyperlinkButton.h:78 The colour to use for the URL text. + { 0x1000100, "buttonColourId" }, // juce_TextButton.h:73 The colour used to fill the button shape (when the button is toggled + { 0x1000101, "buttonOnColourId" }, // juce_TextButton.h:76 The colour used to fill the button shape (when the button is toggled + { 0x1000102, "textColourOffId" }, // juce_TextButton.h:79 The colour to use for the button's text when the button's toggle state is "off". + { 0x1000103, "textColourOnId" }, // juce_TextButton.h:80 The colour to use for the button's text.when the button's toggle state is "on". + { 0x1006501, "textColourId" }, // juce_ToggleButton.h:74 The colour to use for the button's text. + { 0x1006502, "tickColourId" }, // juce_ToggleButton.h:75 The colour to use for the tick mark. + { 0x1006503, "tickDisabledColourId" }, // juce_ToggleButton.h:76 The colour to use for the disabled tick mark and/or outline. + { 0x1000540, "highlightColourId" }, // juce_DirectoryContentsDisplayComponent.h:95 The colour to use to fill a highlighted row of the list. + { 0x1000541, "textColourId" }, // juce_DirectoryContentsDisplayComponent.h:96 The colour for the text. + { 0x1000542, "highlightedTextColourId" }, // juce_DirectoryContentsDisplayComponent.h:97 The colour with which to draw the text in highlighted sections. + { 0x1000640, "currentPathBoxBackgroundColourId" }, // juce_FileBrowserComponent.h:225 The colour to use to fill the background of the current path ComboBox. + { 0x1000641, "currentPathBoxTextColourId" }, // juce_FileBrowserComponent.h:226 The colour to use for the text of the current path ComboBox. + { 0x1000642, "currentPathBoxArrowColourId" }, // juce_FileBrowserComponent.h:227 The colour to use to draw the arrow of the current path ComboBox. + { 0x1000643, "filenameBoxBackgroundColourId" }, // juce_FileBrowserComponent.h:228 The colour to use to fill the background of the filename TextEditor. + { 0x1000644, "filenameBoxTextColourId" }, // juce_FileBrowserComponent.h:229 The colour to use for the text of the filename TextEditor. + { 0x1000850, "titleTextColourId" }, // juce_FileChooserDialogBox.h:142 The colour to use to draw the box's title. + { 0x1004100, "backgroundColourId" }, // juce_FileSearchPathListComponent.h:70 The background colour to fill the component with. + { 0x1000204, "caretColourId" }, // juce_CaretComponent.h:65 The colour with which to draw the caret. + { 0x1005400, "outlineColourId" }, // juce_GroupComponent.h:80 The colour to use for drawing the line around the edge. + { 0x1005410, "textColourId" }, // juce_GroupComponent.h:81 The colour to use to draw the text label. + { 0x1000300, "backgroundColourId" }, // juce_ScrollBar.h:294 The background colour of the scrollbar. + { 0x1000400, "thumbColourId" }, // juce_ScrollBar.h:295 A base colour to use for the thumb. The look and feel will probably use variations on this colour. + { 0x1000401, "trackColourId" }, // juce_ScrollBar.h:296 A base colour to use for the slot area of the bar. The look and feel will probably use variations on this colour. + { 0x100f001, "backgroundColour" }, // juce_SidePanel.h:170 + { 0x100f002, "titleTextColour" }, // juce_SidePanel.h:171 + { 0x100f003, "shadowBaseColour" }, // juce_SidePanel.h:172 + { 0x100f004, "dismissButtonNormalColour" }, // juce_SidePanel.h:173 + { 0x100f005, "dismissButtonOverColour" }, // juce_SidePanel.h:174 + { 0x100f006, "dismissButtonDownColour" }, // juce_SidePanel.h:175 + { 0x1005812, "tabOutlineColourId" }, // juce_TabbedButtonBar.h:296 The colour to use to draw an outline around the tabs. + { 0x1005813, "tabTextColourId" }, // juce_TabbedButtonBar.h:297 The colour to use to draw the tab names. If this isn't specified, + { 0x1005814, "frontOutlineColourId" }, // juce_TabbedButtonBar.h:299 The colour to use to draw an outline around the currently-selected tab. + { 0x1005815, "frontTextColourId" }, // juce_TabbedButtonBar.h:300 The colour to use to draw the currently-selected tab name. If + { 0x1005800, "backgroundColourId" }, // juce_TabbedComponent.h:188 The colour to fill the background behind the tabs. + { 0x1005801, "outlineColourId" }, // juce_TabbedComponent.h:189 The colour to use to draw an outline around the content. + { 0x1000700, "backgroundColourId" }, // juce_PopupMenu.h:760 The colour to fill the menu's background with. + { 0x1000600, "textColourId" }, // juce_PopupMenu.h:761 The colour for normal menu item text, (unless the + { 0x1000601, "headerTextColourId" }, // juce_PopupMenu.h:763 The colour for section header item text (see the + { 0x1000900, "highlightedBackgroundColourId" }, // juce_PopupMenu.h:765 The colour to fill the background of the currently + { 0x1000800, "highlightedTextColourId" }, // juce_PopupMenu.h:767 The colour to use for the text of the currently + { 0x1000af0, "backgroundColourId" }, // juce_BubbleComponent.h:142 A background colour to fill the bubble with. + { 0x1000af1, "outlineColourId" }, // juce_BubbleComponent.h:143 The colour to use for an outline around the bubble. + { 0x1000440, "lassoFillColourId" }, // juce_LassoComponent.h:196 The colour to fill the lasso rectangle with. + { 0x1000441, "lassoOutlineColourId" }, // juce_LassoComponent.h:197 The colour to draw the outline with. + { 0x100e801, "backgroundColourId" }, // juce_BooleanPropertyComponent.h:92 The colour to fill the background of the button area. + { 0x100e803, "outlineColourId" }, // juce_BooleanPropertyComponent.h:93 The colour to use to draw an outline around the text area. + { 0x1008300, "backgroundColourId" }, // juce_PropertyComponent.h:117 The background colour to fill the component with. + { 0x1008301, "labelTextColourId" }, // juce_PropertyComponent.h:118 The colour for the property's label text. + { 0x100e401, "backgroundColourId" }, // juce_TextPropertyComponent.h:121 The colour to fill the background of the text area. + { 0x100e402, "textColourId" }, // juce_TextPropertyComponent.h:122 The colour to use for the editable text. + { 0x100e403, "outlineColourId" }, // juce_TextPropertyComponent.h:123 The colour to use to draw an outline around the text area. + { 0x1000b00, "backgroundColourId" }, // juce_ComboBox.h:356 The background colour to fill the box with. + { 0x1000a00, "textColourId" }, // juce_ComboBox.h:357 The colour for the text in the box. + { 0x1000c00, "outlineColourId" }, // juce_ComboBox.h:358 The colour for an outline around the box. + { 0x1000d00, "buttonColourId" }, // juce_ComboBox.h:359 The base colour for the button (a LookAndFeel class will probably use variations on this). + { 0x1000e00, "arrowColourId" }, // juce_ComboBox.h:360 The colour for the arrow shape that pops up the menu + { 0x1000f00, "focusedOutlineColourId" }, // juce_ComboBox.h:361 The colour that will be used to draw a box around the edge of the component when it has focus. + { 0x1000280, "backgroundColourId" }, // juce_Label.h:106 The background colour to fill the label with. + { 0x1000281, "textColourId" }, // juce_Label.h:107 The colour for the text. + { 0x1000282, "outlineColourId" }, // juce_Label.h:108 An optional colour to use to draw a border around the label. + { 0x1000283, "backgroundWhenEditingColourId" }, // juce_Label.h:110 The background colour when the label is being edited. + { 0x1000284, "textWhenEditingColourId" }, // juce_Label.h:111 The colour for the text when the label is being edited. + { 0x1000285, "outlineWhenEditingColourId" }, // juce_Label.h:112 An optional border colour when the label is being edited. + { 0x1002800, "backgroundColourId" }, // juce_ListBox.h:504 The background colour to fill the list with. + { 0x1002810, "outlineColourId" }, // juce_ListBox.h:506 An optional colour to use to draw a border around the list. + { 0x1002820, "textColourId" }, // juce_ListBox.h:508 The preferred colour to use for drawing text in the listbox. + { 0x1001900, "backgroundColourId" }, // juce_ProgressBar.h:144 The background colour, behind the bar. + { 0x1001a00, "foregroundColourId" }, // juce_ProgressBar.h:145 The colour to use to draw the bar itself. LookAndFeel + { 0x1001200, "backgroundColourId" }, // juce_Slider.h:869 A colour to use to fill the slider's background. + { 0x1001300, "thumbColourId" }, // juce_Slider.h:870 The colour to draw the thumb with. It's up to the look + { 0x1001310, "trackColourId" }, // juce_Slider.h:872 The colour to draw the groove that the thumb moves along. + { 0x1001311, "rotarySliderFillColourId" }, // juce_Slider.h:873 For rotary sliders, this colour fills the outer curve. + { 0x1001312, "rotarySliderOutlineColourId" }, // juce_Slider.h:874 For rotary sliders, this colour is used to draw the outer curve's outline. + { 0x1001400, "textBoxTextColourId" }, // juce_Slider.h:876 The colour for the text in the text-editor box used for editing the value. + { 0x1001500, "textBoxBackgroundColourId" }, // juce_Slider.h:877 The background colour for the text-editor box. + { 0x1001600, "textBoxHighlightColourId" }, // juce_Slider.h:878 The text highlight colour for the text-editor box. + { 0x1001700, "textBoxOutlineColourId" }, // juce_Slider.h:879 The colour to use for a border around the text-editor box. + { 0x1003800, "textColourId" }, // juce_TableHeaderComponent.h:377 The colour for the text in the header. + { 0x1003810, "backgroundColourId" }, // juce_TableHeaderComponent.h:378 The colour of the table header background. + { 0x1003820, "outlineColourId" }, // juce_TableHeaderComponent.h:380 The colour of the table header's outline. + { 0x1003830, "highlightColourId" }, // juce_TableHeaderComponent.h:381 The colour of the table header background when + { 0x1000200, "backgroundColourId" }, // juce_TextEditor.h:209 The colour to use for the text component's background - this can be + { 0x1000201, "textColourId" }, // juce_TextEditor.h:212 The colour that will be used when text is added to the editor. Note + { 0x1000202, "highlightColourId" }, // juce_TextEditor.h:217 The colour with which to fill the background of highlighted sections of + { 0x1000203, "highlightedTextColourId" }, // juce_TextEditor.h:221 The colour with which to draw the text in highlighted sections. + { 0x1000205, "outlineColourId" }, // juce_TextEditor.h:223 If this is non-transparent, it will be used to draw a box around + { 0x1000206, "focusedOutlineColourId" }, // juce_TextEditor.h:226 If this is non-transparent, it will be used to draw a box around + { 0x1000207, "shadowColourId" }, // juce_TextEditor.h:229 If this is non-transparent, it'll be used to draw an inner shadow + { 0x1003200, "backgroundColourId" }, // juce_Toolbar.h:237 A colour to use to fill the toolbar's background. For + { 0x1003210, "separatorColourId" }, // juce_Toolbar.h:239 A colour to use to draw the separator lines. + { 0x1003220, "buttonMouseOverBackgroundColourId" }, // juce_Toolbar.h:241 A colour used to paint the background of buttons when the mouse is + { 0x1003230, "buttonMouseDownBackgroundColourId" }, // juce_Toolbar.h:243 A colour used to paint the background of buttons when the mouse is + { 0x1003240, "labelTextColourId" }, // juce_Toolbar.h:246 A colour to use for drawing the text under buttons + { 0x1003250, "editingModeOutlineColourId" }, // juce_Toolbar.h:249 A colour to use for an outline around buttons when + { 0x1000500, "backgroundColourId" }, // juce_TreeView.h:870 A background colour to fill the component with. + { 0x1000501, "linesColourId" }, // juce_TreeView.h:871 The colour to draw the lines with. + { 0x1000502, "dragAndDropIndicatorColourId" }, // juce_TreeView.h:872 The colour to use for the drag-and-drop target position indicator. + { 0x1000503, "selectedItemBackgroundColourId" }, // juce_TreeView.h:873 The colour to use to fill the background of any selected items. + { 0x1000504, "oddItemsColourId" }, // juce_TreeView.h:874 The colour to use to fill the background of the odd numbered items. + { 0x1000505, "evenItemsColourId" }, // juce_TreeView.h:875 The colour to use to fill the background of the even numbered items. + { 0x1001800, "backgroundColourId" }, // juce_AlertWindow.h:501 The background colour for the window. + { 0x1001810, "textColourId" }, // juce_AlertWindow.h:502 The colour for the text. + { 0x1001820, "outlineColourId" }, // juce_AlertWindow.h:503 An optional colour to use to draw a border around the window. + { 0x1005701, "textColourId" }, // juce_DocumentWindow.h:236 The colour to draw any text with. It's up to the look + { 0x1005700, "backgroundColourId" }, // juce_ResizableWindow.h:312 A colour to use to fill the window's background. + { 0x1001b00, "backgroundColourId" }, // juce_TooltipWindow.h:114 The colour to fill the background with. + { 0x1001c00, "textColourId" }, // juce_TooltipWindow.h:115 The colour to use for the text. + { 0x1001c10, "outlineColourId" }, // juce_TooltipWindow.h:116 The colour to use to draw an outline around the tooltip. + }; + +} // namespace melatonin::colors diff --git a/modules/melatonin_inspector/melatonin/helpers/component_helpers.h b/modules/melatonin_inspector/melatonin/helpers/component_helpers.h new file mode 100644 index 00000000..ebecd1e4 --- /dev/null +++ b/modules/melatonin_inspector/melatonin/helpers/component_helpers.h @@ -0,0 +1,113 @@ +#pragma once +#include + +#if !defined(_MSC_VER) + #include +namespace melatonin +{ + // https://stackoverflow.com/a/4541470 + static inline std::string demangle (const char* name) + { + int status = -4; // some arbitrary value to eliminate the compiler warning + + std::unique_ptr res { + abi::__cxa_demangle (name, nullptr, nullptr, &status), + std::free + }; + + return (status == 0) ? res.get() : name; + } + + template + static inline juce::String type (const T& t) + { + return demangle (typeid (t).name()); + } +} +#else +namespace melatonin +{ + template + static inline juce::String type (const T& t) + { + return juce::String (typeid (t).name()).replace ("class ", "").replace ("struct ", ""); + } +} +#endif +namespace melatonin +{ + // do our best to derive a useful UI string from a component + static inline juce::String componentString (juce::Component* c) + { + if (auto* label = dynamic_cast (c)) + { + return juce::String ("Label: ") + label->getText().substring (0, 20); + } +#if JUCE_MODULE_AVAILABLE_juce_audio_processors + else if (auto editor = dynamic_cast (c)) + { + return juce::String ("Editor: ") + editor->getAudioProcessor()->getName(); + } +#endif + else if (c && c->isAccessible() && c->getAccessibilityHandler() && !c->getAccessibilityHandler()->getTitle().isEmpty()) + { + auto acctitle = c->getAccessibilityHandler()->getTitle(); + return acctitle; + } + else if (c && !c->getName().isEmpty()) + { + return c->getName(); + } + else if (c) + { + return type (*c); + } + else + { + return ""; + } + } + + // do our best to derive a useful UI string from a component + static inline juce::String componentFontValue (juce::Component* c) + { + if (auto* label = dynamic_cast (c)) + { + auto font = label->getFont(); + return juce::String (font.getTypefaceName() + " " + font.getTypefaceStyle() + " " + juce::String (font.getHeight())); + } + else if (auto btn = dynamic_cast (c)) + { + auto font = btn->getLookAndFeel().getTextButtonFont (*btn, btn->getHeight()); + return juce::String (font.getTypefaceName() + " " + font.getTypefaceStyle() + " " + juce::String (font.getHeight())); + } + else if (auto slider = dynamic_cast (c)) + { + auto font = slider->getLookAndFeel().getSliderPopupFont (*slider); + return juce::String (font.getTypefaceName() + " " + font.getTypefaceStyle() + " " + juce::String (font.getHeight())); + } + else if (auto cb = dynamic_cast (c)) + { + auto font = cb->getLookAndFeel().getComboBoxFont (*cb); + return juce::String (font.getTypefaceName() + " " + font.getTypefaceStyle() + " " + juce::String (font.getHeight())); + } + else + { + return "-"; + } + } + + // returns name of assigned LnF + static inline juce::String lnfString (juce::Component* c) + { + if (c) + { + auto& lnf = c->getLookAndFeel(); + return type (lnf); + } + else + { + return "-"; + } + } +} diff --git a/modules/melatonin_inspector/melatonin/helpers/inspector_settings.h b/modules/melatonin_inspector/melatonin/helpers/inspector_settings.h new file mode 100644 index 00000000..b3f5d7f5 --- /dev/null +++ b/modules/melatonin_inspector/melatonin/helpers/inspector_settings.h @@ -0,0 +1,33 @@ +#pragma once +#include "juce_gui_basics/juce_gui_basics.h" +namespace melatonin +{ + // We have to avoid using ApplicationProperties in a SharedResourcePointer + // as we don't want to screw up the app's properties file + struct InspectorSettings + { + InspectorSettings() + { + juce::PropertiesFile::Options opts; + + opts.applicationName = "melatonin_inspector"; + opts.filenameSuffix = ".xml"; + opts.folderName = "sudara"; + opts.osxLibrarySubFolder = "Application Support"; + opts.commonToAllUsers = false; + opts.ignoreCaseOfKeyNames = false; + opts.doNotSave = false; + opts.millisecondsBeforeSaving = 1; + opts.storageFormat = juce::PropertiesFile::storeAsXML; + props = std::make_unique (opts); + } + + void saveIfNeeded() const + { + props->saveIfNeeded(); + } + + // this is a unique_ptr because our object must be default constructable + std::unique_ptr props; + }; +} diff --git a/modules/melatonin_inspector/melatonin/helpers/misc.h b/modules/melatonin_inspector/melatonin/helpers/misc.h new file mode 100644 index 00000000..afe5d4f6 --- /dev/null +++ b/modules/melatonin_inspector/melatonin/helpers/misc.h @@ -0,0 +1,38 @@ +#pragma once +//#include "InspectorBinaryData.h" // uncomment when adding assets via CMake +#include "../../LatestCompiledAssets/InspectorBinaryData.h" +#include + +namespace melatonin +{ + static inline juce::String dimensionsString (juce::Rectangle bounds) + { + // account for outline border drawing (1 on each side) + return juce::String (bounds.getWidth() - 2) + L" × " + juce::String (bounds.getHeight() - 2); + } + + static inline juce::String distanceString (juce::Line line) + { + // account for outline border drawing (1 on each side) + auto v = line.getLength() + 3; + return juce::String (v); + } + + static inline juce::Image getIcon (const juce::String& iconName) + { + int size = 0; + auto filename = iconName.replace (" ", "") + "_png"; + auto data = InspectorBinaryData::getNamedResource (filename.getCharPointer(), size); + if (size > 0) + { + return juce::ImageCache::getFromMemory (data, size); + } + else + { + // you told me to load an image that doesn't exist + jassertfalse; + return {}; + } + } + +} diff --git a/modules/melatonin_inspector/melatonin/helpers/overlay_mouse_listener.h b/modules/melatonin_inspector/melatonin/helpers/overlay_mouse_listener.h new file mode 100644 index 00000000..eba5a3f9 --- /dev/null +++ b/modules/melatonin_inspector/melatonin/helpers/overlay_mouse_listener.h @@ -0,0 +1,121 @@ +#pragma once +#include "juce_gui_basics/juce_gui_basics.h" + +namespace melatonin +{ + // Unfortunately the DocumentWindow cannot behave as our root component mouse listener + // without some strange side effects. That's why we are doing the whole lambda dance... + class OverlayMouseListener : public juce::MouseListener + { + public: + OverlayMouseListener() + { + } + + ~OverlayMouseListener() override + { + if (enabled && root) + root->removeMouseListener (this); + } + + void setRoot (juce::Component& c) + { + root = &c; + + if (enabled) + root->addMouseListener (this, true); + } + + void clearRoot() + { + if (enabled && root) + root->removeMouseListener (this); + + root = nullptr; + } + + void enable() + { + // replace me with an if condition if you are hitting this on purpose + jassert (!enabled); + enabled = true; + root->addMouseListener (this, true); + } + + void disable() + { + if (enabled) + { + enabled = false; + root->removeMouseListener (this); + } + } + + void mouseEnter (const juce::MouseEvent& event) override + { + outlineComponentCallback (event.originalComponent); + } + + void mouseMove (const juce::MouseEvent& event) override + { + if (outlineDistanceCallback && event.mods.isAltDown()) + outlineDistanceCallback (event.originalComponent); + else + outlineDistanceCallback (nullptr); + } + + void mouseUp (const juce::MouseEvent& event) override + { + if (event.mods.isLeftButtonDown() && !isDragging) + { + selectComponentCallback (event.originalComponent); + } + isDragging = false; + } + + void mouseDown (const juce::MouseEvent& event) override + { + if (event.mods.isLeftButtonDown() && event.originalComponent->isMouseOverOrDragging()) + { + componentStartDraggingCallback (event.originalComponent, event); + } + } + + void mouseDrag (const juce::MouseEvent& event) override + { + // takes care of small mouse position drift on selection + if (event.getDistanceFromDragStart() > 3 && event.originalComponent->isMouseOverOrDragging()) + { + isDragging = true; + componentDraggedCallback (event.originalComponent, event); + } + } + + void mouseExit (const juce::MouseEvent& event) override + { + if (event.originalComponent == root) + { + // TODO: Sudara is wondering if this callback is needed... + mouseExitCallback(); + } + + // not sure if there's a better way to ask "is the mouse outside the plugin now?" + if (!root->contains (event.getEventRelativeTo (root).position)) + outlineComponentCallback (nullptr); + } + + std::function outlineComponentCallback; + std::function outlineDistanceCallback; + std::function selectComponentCallback; + std::function componentStartDraggingCallback; + std::function componentDraggedCallback; + std::function mouseExitCallback; + + private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OverlayMouseListener) + + juce::Component* root = nullptr; + bool enabled = false; + bool isDragging { false }; + }; +} diff --git a/modules/melatonin_inspector/melatonin/helpers/timing.h b/modules/melatonin_inspector/melatonin/helpers/timing.h new file mode 100644 index 00000000..b7616b85 --- /dev/null +++ b/modules/melatonin_inspector/melatonin/helpers/timing.h @@ -0,0 +1,38 @@ +#pragma once +#include + +namespace melatonin +{ + class ComponentTimer + { + public: + explicit ComponentTimer (juce::Component* c) : component (c) + { + startTimeTicks = juce::Time::getHighResolutionTicks(); + } + + ~ComponentTimer() + { + static double scalar = 1.0 / static_cast (juce::Time::getHighResolutionTicksPerSecond()); + result = static_cast (juce::Time::getHighResolutionTicks() - startTimeTicks) * scalar; + + auto& props = component->getProperties(); + + // if this new time is slower, make it the max + if (result > (double) props.getWithDefault ("timing1", 0.0)) + props.set ("timingMax", result); + + // push the 1st and 2nd timings down + props.set ("timing3", props.getWithDefault ("timing2", 0.0)); + props.set ("timing2", props.getWithDefault ("timing1", 0.0)); + props.set ("timing1", result); + } + + private: + juce::Component* component; + juce::int64 startTimeTicks; + double result = 0; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentTimer) + }; + +} diff --git a/modules/melatonin_inspector/melatonin/inspector_component.h b/modules/melatonin_inspector/melatonin/inspector_component.h new file mode 100644 index 00000000..d3e40b6c --- /dev/null +++ b/modules/melatonin_inspector/melatonin/inspector_component.h @@ -0,0 +1,438 @@ +#pragma once + +#include "components/inspector_image_button.h" +#include "helpers/misc.h" +#include "melatonin_inspector/melatonin/components/accesibility.h" +#include "melatonin_inspector/melatonin/components/box_model.h" +#include "melatonin_inspector/melatonin/components/color_picker.h" +#include "melatonin_inspector/melatonin/components/component_tree_view_item.h" +#include "melatonin_inspector/melatonin/components/preview.h" +#include "melatonin_inspector/melatonin/components/properties.h" +#include "melatonin_inspector/melatonin/lookandfeel.h" + +/* + * Right now this unfortunately bundles all inspector components + * as well as the tree view and selection logic. + */ + +namespace melatonin +{ + class InspectorComponent : public juce::Component + { + public: + explicit InspectorComponent() + { + TRACE_COMPONENT(); + + setMouseClickGrabsKeyboardFocus (false); + + addAndMakeVisible (enabledButton); + addAndMakeVisible (logo); + addAndMakeVisible (fpsToggle); + addAndMakeVisible (moveToggle); + addAndMakeVisible (tabToggle); + + addChildComponent (tree); + addChildComponent (emptySearchLabel); + + // visibilities of these are managed by the panels above + addChildComponent (boxModel); + addChildComponent (colorPicker); + addChildComponent (preview); + addChildComponent (properties); + addChildComponent (accessibility); + + // z-order on panels is higher so they are clickable + addAndMakeVisible (boxModelPanel); + addAndMakeVisible (colorPickerPanel); + addAndMakeVisible (previewPanel); + addAndMakeVisible (propertiesPanel); + addAndMakeVisible (accessibilityPanel); + + addAndMakeVisible (searchBox); + addAndMakeVisible (searchIcon); + addChildComponent (clearButton); + + colorPicker.togglePickerCallback = [this] (bool value) { + if (toggleOverlayCallback) + { + // re-enabling the color picker re-enables the overlay too quickly + // resulting in an unwanted click on the overlay and selection + if (value) + { + juce::Timer::callAfterDelay (500, [this] { toggleOverlayCallback (true); }); + } + else + toggleOverlayCallback (false); + } + }; + + emptySelectionPrompt.setJustificationType (juce::Justification::centredTop); + emptySearchLabel.setJustificationType (juce::Justification::centredTop); + emptySearchLabel.setColour (juce::Label::textColourId, colors::treeItemTextSelected); + + // the JUCE widget is unfriendly for theming, so indenting is also manually handled + tree.setIndentSize (12); + + // JUCE makes it impossible to add any vertical padding within the viewport + tree.getViewport()->setViewPosition (0, 0); + tree.getViewport()->setScrollBarThickness (20); + + searchBox.setHelpText ("search"); + searchBox.setFont (juce::Font ("Verdana", 17, juce::Font::FontStyleFlags::plain)); + searchBox.setColour (juce::Label::backgroundColourId, juce::Colours::transparentBlack); + searchBox.setColour (juce::Label::textColourId, colors::treeItemTextSelected); + searchBox.setColour (juce::TextEditor::outlineColourId, juce::Colours::transparentBlack); + searchBox.setColour (juce::TextEditor::focusedOutlineColourId, juce::Colours::transparentBlack); + searchBox.setTextToShowWhenEmpty ("Filter components...", colors::searchText); + searchBox.setJustification (juce::Justification::centredLeft); + searchBox.onEscapeKey = [&] { + searchBox.setText (""); + searchBox.giveAwayKeyboardFocus(); + lastSearchText = {}; + getRoot()->validateSubItems(); + }; + + logo.onClick = []() { juce::URL ("https://github.com/sudara/melatonin_inspector/").launchInDefaultBrowser(); }; + searchBox.onTextChange = [this] { + auto searchText = searchBox.getText(); + ensureTreeIsConstructed(); + + if (lastSearchText.isNotEmpty() && !searchText.startsWith (lastSearchText)) + { + getRoot()->validateSubItems(); + } + + lastSearchText = searchText; + + // try to find the first item that matches the search string + if (searchText.isNotEmpty()) + { + getRoot()->filterNodesRecursively (searchText); + } + + // display empty label + if (getRoot()->getNumSubItems() == 0 + && !searchText.containsIgnoreCase (getRoot()->getComponentName()) + && tree.getNumSelectedItems() == 0) + { + tree.setVisible (false); + emptySearchLabel.setVisible (true); + + resized(); + } + else + { + tree.setVisible (true); + emptySearchLabel.setVisible (false); + } + + clearButton.setVisible (searchBox.getText().isNotEmpty()); + }; + + enabledButton.on = inspectorEnabled; + enabledButton.onClick = [this] { + toggleCallback (!inspectorEnabled); + }; + + // TODO: refactor this "on" state, it's terribly named + fpsToggle.on = false; + fpsToggle.onClick = [this] { + // TODO: I don't like that the "on" state implicitly was toggled here + settings->props->setValue ("fpsEnabled", fpsToggle.on); + toggleFPSCallback (fpsToggle.on); + }; + + clearButton.onClick = [this] { + searchBox.setText (""); + searchBox.giveAwayKeyboardFocus(); + }; + + // TODO: sorta sketchy to "know" the enum default... + tabToggle.on = settings->props->getIntValue ("selectionMode", 0); + tabToggle.onClick = [this] { + settings->props->setValue ("selectionMode", fpsToggle.on); + toggleSelectionMode (tabToggle.on); + }; + + // the tree view is empty even if inspector is enabled + // since at the moment when this panel getting initialized, the root component most likely doesn't have any children YET + // we can either wait and launch async update or add empty label + } + + ~InspectorComponent() override + { + tree.setRootItem (nullptr); + } + + void setRoot (juce::Component& r) + { + root = &r; + colorPicker.setRootComponent (root); + + tree.setRootItem (nullptr); + rootItem = nullptr; + + if (inspectorEnabled) + ensureTreeIsConstructed(); + } + + void clearRoot() + { + root = nullptr; + colorPicker.setRootComponent (nullptr); + } + + void paint (juce::Graphics& g) override + { + auto mainPanelGradient = juce::ColourGradient::horizontal (colors::panelBackgroundDarker, (float) mainColumnBounds.getX(), colors::panelBackgroundLighter, (float) mainColumnBounds.getWidth()); + g.setGradientFill (mainPanelGradient); + g.fillRect (mainColumnBounds); + + g.setColour (colors::headerBackground); + g.fillRect (topArea); + + g.setColour (colors::black); + g.fillRect (searchBoxBounds.expanded (0, 2)); + + auto treeGradient = juce::ColourGradient::horizontal (colors::treeBackgroundLighter, (float) treeViewBounds.getX(), colors::treeBackgroundDarker, (float) treeViewBounds.getWidth()); + g.setGradientFill (treeGradient); + g.fillRect (treeViewBounds); + } + + void ensureTreeIsConstructed() + { + TRACE_COMPONENT(); + + jassert (selectComponentCallback); + + // don't perform unnecessary work + if (rootItem && rootItem.get() == getRoot()) + return; + + // if the root was set to something else, wipe it + else if (rootItem) + tree.setRootItem (nullptr); + + // construct the root item + rootItem = std::make_unique (root, outlineComponentCallback, selectComponentCallback); + tree.setRootItem (rootItem.get()); + getRoot()->setOpenness (ComponentTreeViewItem::Openness::opennessOpen); + + tree.setVisible (true); + auto numComponents = getRoot()->countItemsRecursively(); + searchBox.setTextToShowWhenEmpty (juce::String ("Filter " + juce::String (numComponents) + " components..."), colors::searchText); + + resized(); + } + + void resized() override + { + TRACE_COMPONENT(); + + auto area = getLocalBounds(); + + if (!inspectorEnabled) + mainColumnBounds = area.removeFromLeft (380); + else + mainColumnBounds = area.removeFromRight (juce::jmax (380, int ((float) area.getWidth() * 0.6f))); + + auto mainCol = mainColumnBounds; + auto headerHeight = 48; + + topArea = mainCol.removeFromTop (headerHeight); + auto toolbar = topArea; + enabledButton.setBounds (toolbar.removeFromLeft (48)); + fpsToggle.setBounds (toolbar.removeFromLeft (42)); + tabToggle.setBounds (toolbar.removeFromLeft (42)); + logo.setBounds (toolbar.removeFromRight (56)); + + mainCol.removeFromTop (12); + boxModelPanel.setBounds (mainCol.removeFromTop (32)); + boxModel.setBounds (mainCol.removeFromTop (boxModel.isVisible() ? 280 : 0)); + + auto previewHeight = (preview.showsPerformanceTimings()) ? 182 : 132; + auto previewBounds = mainCol.removeFromTop (preview.isVisible() ? previewHeight : 32); + preview.setBounds (previewBounds); + previewPanel.setBounds (previewBounds.removeFromTop (32).removeFromLeft (200)); + + // the picker icon + rgba toggle overlays the panel header, so we overlap it + auto colorPickerHeight = 72; + int numColorsToDisplay = juce::jlimit (0, properties.isVisible() ? 12 : 3, (int) model.colors.size()); + if (colorPicker.isVisible() && !model.colors.empty()) + colorPickerHeight += 24 * numColorsToDisplay; + auto colorPickerBounds = mainCol.removeFromTop (colorPicker.isVisible() ? colorPickerHeight : 32); + colorPicker.setBounds (colorPickerBounds.withTrimmedLeft (32)); + colorPickerPanel.setBounds (colorPickerBounds.removeFromTop (32).removeFromLeft (200)); + + accessibilityPanel.setBounds (mainCol.removeFromTop (32)); + accessibility.setBounds (mainCol.removeFromTop (accessibility.isVisible() ? 110 : 0).withTrimmedLeft (32)); + + propertiesPanel.setBounds (mainCol.removeFromTop (33)); // extra pixel for divider + properties.setBounds (mainCol.withTrimmedLeft (32)); + + searchBoxBounds = area.removeFromTop (headerHeight); + auto b = searchBoxBounds; + clearButton.setBounds (b.removeFromRight (48)); + searchIcon.setBounds (b.removeFromLeft (48)); + searchBox.setBounds (b.reduced (0, 2)); + + emptySearchLabel.setBounds (searchBoxBounds.reduced (4, 24)); + + // these bounds are used to paint the background + treeViewBounds = area; + tree.setBounds (treeViewBounds); + } + + void displayComponentInfo (Component* component, bool collapseTreeBeforeSelection = false) + { + TRACE_COMPONENT(); + + ensureTreeIsConstructed(); + + // only show on hover if there isn't something selected + if (!selectedComponent || selectedComponent == component) + { + model.selectComponent (component); + + resized(); + repaint(); + + // Selects and highlights + if (component && getRoot()) + { + if (collapseTreeBeforeSelection) + getRoot()->recursivelyCloseSubItems(); + + getRoot()->openTreeAndSelect (component); + tree.scrollToKeepItemVisible (tree.getSelectedItem (0)); + } + } + } + + void redisplaySelectedComponent() + { + if (selectedComponent) + { + displayComponentInfo (selectedComponent); + } + } + + void selectComponent (Component* component, bool collapseTreeBeforeSelection = false) + { + TRACE_COMPONENT(); + + if (component && selectedComponent == component) + { + deselectComponent(); + return; + } + selectedComponent = component; + displayComponentInfo (selectedComponent, collapseTreeBeforeSelection); + } + + void deselectComponent() + { + TRACE_COMPONENT(); + + selectedComponent = nullptr; + tree.clearSelectedItems(); + + properties.reset(); + model.deselectComponent(); + tree.setRootItem (getRoot()); + + preview.repaint(); + colorPicker.reset(); + + resized(); + } + + // called from the main melatonin_inspector.h + // for example, on load of the entire inspector + // or after a toggle button click + void toggle (bool nowEnabled) + { + TRACE_COMPONENT(); + + enabledButton.on = nowEnabled; + inspectorEnabled = nowEnabled; + + // content visibility is handled by the panel + previewPanel.setVisible (nowEnabled); + colorPickerPanel.setVisible (nowEnabled); + propertiesPanel.setVisible (nowEnabled); + tree.setVisible (nowEnabled); + + if (!nowEnabled) + { + model.deselectComponent(); + if (getRoot()) + getRoot()->recursivelyCloseSubItems(); + } + + // populate the tree view if nothing selected + else if (selectedComponent == nullptr) + ensureTreeIsConstructed(); + + colorPicker.reset(); + + resized(); + } + + std::function selectComponentCallback; + std::function outlineComponentCallback; + std::function toggleCallback; + std::function toggleOverlayCallback; + std::function toggleFPSCallback; + std::function toggleSelectionMode; + + private: + Component::SafePointer selectedComponent; + Component* root = nullptr; + juce::SharedResourcePointer settings; + ComponentModel model; + bool inspectorEnabled = false; + + juce::Rectangle mainColumnBounds, topArea, searchBoxBounds, treeViewBounds; + InspectorImageButton logo { "logo" }; + + BoxModel boxModel { model }; + CollapsablePanel boxModelPanel { "BOX MODEL", &boxModel }; + + Preview preview { model }; + CollapsablePanel previewPanel { "PREVIEW", &preview }; + + ColorPicker colorPicker { model, preview }; + CollapsablePanel colorPickerPanel { "COLORS", &colorPicker }; + + Properties properties { model }; + CollapsablePanel propertiesPanel { "PROPERTIES", &properties, true }; + + Accessibility accessibility { model }; + CollapsablePanel accessibilityPanel { "ACCESSIBILITY", &accessibility, false }; + + // TODO: move to its own component + juce::TreeView tree; + juce::Label emptySelectionPrompt { "SelectionPrompt", "Select any component to see components tree" }; + juce::Label emptySearchLabel { "EmptySearchResultsPrompt", "No component found" }; + juce::TextEditor searchBox { "Search box" }; + InspectorImageButton clearButton { "clear", { 0, 6 } }; + InspectorImageButton searchIcon { "search", { 8, 8 } }; + InspectorImageButton enabledButton { "enabled", { 8, 6 }, true }; + InspectorImageButton fpsToggle { "speedometer", { 2, 7 }, true }; + InspectorImageButton moveToggle { "move", { 0, 6 }, true }; + InspectorImageButton tabToggle { "tab", { 0, 6 }, true }; + + juce::String lastSearchText; + + std::unique_ptr rootItem; + + ComponentTreeViewItem* getRoot() + { + return dynamic_cast (tree.getRootItem()); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InspectorComponent) + }; +} diff --git a/modules/melatonin_inspector/melatonin/lookandfeel.h b/modules/melatonin_inspector/melatonin/lookandfeel.h new file mode 100644 index 00000000..100b063f --- /dev/null +++ b/modules/melatonin_inspector/melatonin/lookandfeel.h @@ -0,0 +1,233 @@ +#pragma once + +#include "helpers/colors.h" +#include "juce_gui_extra/juce_gui_extra.h" + +namespace melatonin +{ + + class InspectorLookAndFeel : public juce::LookAndFeel_V4 + { + public: + InspectorLookAndFeel() + { + // often the app overrides this + setColour (juce::Label::outlineWhenEditingColourId, colors::highlight); + + setColour (juce::Label::textColourId, colors::label); + setColour (juce::TextEditor::textColourId, colors::label); + setColour (juce::TextEditor::backgroundColourId, juce::Colours::transparentBlack); + setColour (juce::TextEditor::highlightColourId, colors::highlightedText); + setColour (juce::CaretComponent::caretColourId, colors::caret); + + setColour (juce::ToggleButton::ColourIds::textColourId, colors::text); + setColour (juce::ToggleButton::ColourIds::tickDisabledColourId, colors::text); + setColour (juce::ToggleButton::ColourIds::tickColourId, colors::highlight); + + setColour (juce::TextButton::buttonColourId, juce::Colours::transparentBlack); + setColour (juce::ComboBox::outlineColourId, juce::Colours::transparentBlack); + + setColour (juce::PropertyComponent::backgroundColourId, juce::Colours::transparentBlack); + setColour (juce::TextEditor::outlineColourId, juce::Colours::transparentBlack); + + setColour (juce::TextPropertyComponent::backgroundColourId, juce::Colours::transparentBlack); + setColour (juce::TextPropertyComponent::outlineColourId, juce::Colours::transparentBlack); + setColour (juce::TextPropertyComponent::textColourId, colors::propertyValue); + setColour (juce::PropertyComponent::ColourIds::labelTextColourId, colors::propertyName); + + setColour (juce::BooleanPropertyComponent::backgroundColourId, juce::Colours::transparentBlack); + setColour (juce::BooleanPropertyComponent::outlineColourId, juce::Colours::transparentBlack); + + // this is transparent so that we can paint it in our item + setColour (juce::TreeView::ColourIds::selectedItemBackgroundColourId, colors::black.withAlpha (0.0f)); + setColour (juce::TreeView::ColourIds::backgroundColourId, juce::Colours::transparentBlack); + + // ColourSelector sliders + setColour (juce::ColourSelector::backgroundColourId, colors::treeBackgroundDarker); + setColour (juce::ColourSelector::labelTextColourId, colors::label); + + setColour (juce::Slider::backgroundColourId, colors::treeBackgroundDarker); + setColour (juce::Slider::thumbColourId, colors::boxModelBoundingBox); + setColour (juce::Slider::trackColourId, colors::boxModelBoundingBox.withAlpha (0.2f)); + setColour (juce::TextEditor::highlightColourId, colors::highlightedText); + setColour (juce::Slider::textBoxBackgroundColourId, juce::Colours::transparentBlack); + setColour (juce::Slider::textBoxHighlightColourId, colors::highlightedText); + setColour (juce::Slider::textBoxOutlineColourId, juce::Colours::transparentBlack); + } + + // we don't want our resizer in the overlay to have a fugly border + void drawResizableFrame (juce::Graphics& g, int w, int h, const juce::BorderSize& border) override + { + ignoreUnused (g, w, h, border); + } + + // For some reason this is actually *needed* which is strange. + // But we want to adjust the color and size of triangles anyway + void drawTreeviewPlusMinusBox (juce::Graphics& g, const juce::Rectangle& area, juce::Colour /*backgroundColour*/, bool isOpen, bool /*isMouseOver*/) override + { + auto tickBounds = area; + + // root component is larger than 28 because of top padding + tickBounds = tickBounds.removeFromBottom (28); + tickBounds.reduce (0, 2); + auto boxSize = juce::jmin (tickBounds.getHeight(), tickBounds.getWidth()); + + juce::Path p; + p.addTriangle (tickBounds.getX() + 1, tickBounds.getY() + boxSize * 0.5f, tickBounds.getX() + boxSize + 1, tickBounds.getY() + boxSize * 0.5f, tickBounds.getX() + boxSize * 0.5f + 1, tickBounds.getY() + boxSize + boxSize * 0.25f); + + g.setColour (colors::treeViewMinusPlusColor); + + auto transform = juce::AffineTransform::rotation (!isOpen ? juce::degreesToRadians (270.0f) : 0, + tickBounds.getCentreX(), + tickBounds.getCentreY()); + + // nothing in JUCE's widget library is properly aligned... + transform = transform.translated (0, 2.0f); + + g.fillPath (p, transform); + } + + // more friendly scrolling + int getDefaultScrollbarWidth() override + { + return 20; + } + + // don't use the target app's font + juce::Font getLabelFont (juce::Label& label) override + { + return { "Verdana", label.getFont().getHeight(), label.getFont().getStyleFlags() }; + } + + // oh i dream of css resets... + juce::BorderSize getLabelBorderSize (juce::Label&) override + { + return juce::BorderSize (0); + } + + // boolean properties and the main "enable inspector" checkbox + void drawToggleButton (juce::Graphics& g, juce::ToggleButton& button, bool /*shouldDrawButtonAsHighlighted*/, bool /*shouldDrawButtonAsDown*/) override + { + float toggleWidth = 14; + float leftPadding = 0; + + juce::Rectangle bounds (leftPadding, (float) button.getHeight() / 2 - toggleWidth / 2.f, toggleWidth, toggleWidth); + + if (button.getToggleState()) + { + g.setColour (colors::highlight); + g.fillRoundedRectangle (bounds, 1.5f); + } + else + { + g.setColour (colors::text); + g.drawRoundedRectangle (bounds, 1, 1.5f); + } + + if (button.getToggleState()) + { + juce::Path tickPath; + tickPath.startNewSubPath (bounds.getX() + 3.5f, bounds.getCentreY() + 1.0f); + tickPath.lineTo (bounds.getCentreX() - 1.0f, bounds.getBottom() - 4.0f); + tickPath.lineTo (bounds.getRight() - 3.5f, bounds.getY() + 4.0f); + + g.setColour (colors::checkboxCheck); + g.strokePath (tickPath, juce::PathStrokeType (2.0f, juce::PathStrokeType::mitered, juce::PathStrokeType::rounded)); + } + + g.setColour (button.findColour (button.getToggleState() ? juce::TextButton::textColourOnId : juce::TextButton::textColourOffId)); + + g.setFont (g.getCurrentFont().withPointHeight (13)); + + g.drawText (button.getButtonText(), + button.getLocalBounds().withTrimmedLeft (juce::roundToInt (toggleWidth) + (int) leftPadding + 12).withTrimmedRight (2), + juce::Justification::centredLeft, + false); + } + + void drawPropertyComponentLabel (juce::Graphics& g, int, int, juce::PropertyComponent& component) override + { + g.setColour (component.findColour (juce::PropertyComponent::labelTextColourId) + .withMultipliedAlpha (component.isEnabled() ? 1.0f : 0.6f)); + + g.setFont (g.getCurrentFont().withPointHeight (13)); + + auto r = getPropertyComponentContentPosition (component); + + g.drawText (component.getName(), + 3, + r.getY(), + r.getX() - 5, + r.getHeight(), + juce::Justification::centredLeft, + true); + + if (component.getProperties().getWithDefault ("isUserProperty", false)) + { + auto textWidth = (float) g.getCurrentFont().getStringWidth (component.getName()); + auto tagBounds = juce::Rectangle (3 + textWidth + 3, 7, 40, 14).toFloat(); + g.setColour (colors::panelBackgroundDarker); + g.fillRoundedRectangle (tagBounds, 3); + g.setColour (colors::highlight); + g.setFont (g.getCurrentFont().withPointHeight (9)); + g.drawText ("PROPS", tagBounds, juce::Justification::centred, false); + } + } + + void drawTextEditorOutline (juce::Graphics& g, int width, int height, juce::TextEditor& textEditor) override + { + if (dynamic_cast (textEditor.getParentComponent()) == nullptr) + { + if (textEditor.isEnabled()) + { + juce::Rectangle b { 0, 0, width, height }; + if (textEditor.hasKeyboardFocus (true) && !textEditor.isReadOnly()) + { + g.setColour (textEditor.findColour (juce::TextEditor::focusedOutlineColourId)); + g.drawRoundedRectangle (b.reduced (1).toFloat(), 0, 1); + } + else + { + g.setColour (textEditor.findColour (juce::TextEditor::outlineColourId)); + g.drawRoundedRectangle (b.reduced (1).toFloat(), 0, 1); + } + } + } + } + + void drawScrollbar (juce::Graphics& g, juce::ScrollBar&, int x, int y, int /*width*/, int height, bool isScrollbarVertical, int thumbStartPosition, int thumbSize, bool, bool) override + { + g.fillAll (colors::scrollbarBackground); + + juce::Rectangle thumbBounds; + if (isScrollbarVertical) + thumbBounds = juce::Rectangle (x + 5, thumbStartPosition, 10, thumbSize); + else + thumbBounds = juce::Rectangle (thumbStartPosition, y, thumbSize, height); + + g.setColour (colors::scrollbar); + g.fillRoundedRectangle (thumbBounds.toFloat(), 2); + } + + void drawCallOutBoxBackground (juce::CallOutBox& box, juce::Graphics& g, const juce::Path& path, juce::Image& cachedImage) override + { + if (cachedImage.isNull()) + { + cachedImage = { juce::Image::ARGB, box.getWidth(), box.getHeight(), true }; + juce::Graphics g2 (cachedImage); + + juce::DropShadow (colors::highlight.withAlpha (0.3f), 12, { 0, 2 }).drawForPath (g2, path); + } + + g.setColour (colors::black); + g.drawImageAt (cachedImage, 0, 0); + + g.setColour (colors::treeBackgroundDarker); + g.fillPath (path); + + g.setColour (colors::highlight.withAlpha (0.7f)); + g.strokePath (path, juce::PathStrokeType (2.0f)); + } + }; + +} diff --git a/modules/melatonin_inspector/melatonin_inspector.cpp b/modules/melatonin_inspector/melatonin_inspector.cpp new file mode 100644 index 00000000..d742f715 --- /dev/null +++ b/modules/melatonin_inspector/melatonin_inspector.cpp @@ -0,0 +1,30 @@ +// WARNING! This file is automatically written by copy_cmake_assets.rb + +// As recommended by the JUCE MODULE API, these cpp files are included by the main module cpp +// See https://github.com/juce-framework/JUCE/blob/master/docs/JUCE%20Module%20Format.md#module-cpp-files + +#include +JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE("-Wredundant-decls") +// NOLINTBEGIN(bugprone-suspicious-include) +#include "LatestCompiledAssets/BinaryData1.cpp" +#include "LatestCompiledAssets/BinaryData10.cpp" +#include "LatestCompiledAssets/BinaryData11.cpp" +#include "LatestCompiledAssets/BinaryData12.cpp" +#include "LatestCompiledAssets/BinaryData13.cpp" +#include "LatestCompiledAssets/BinaryData14.cpp" +#include "LatestCompiledAssets/BinaryData15.cpp" +#include "LatestCompiledAssets/BinaryData16.cpp" +#include "LatestCompiledAssets/BinaryData17.cpp" +#include "LatestCompiledAssets/BinaryData18.cpp" +#include "LatestCompiledAssets/BinaryData19.cpp" +#include "LatestCompiledAssets/BinaryData2.cpp" +#include "LatestCompiledAssets/BinaryData20.cpp" +#include "LatestCompiledAssets/BinaryData3.cpp" +#include "LatestCompiledAssets/BinaryData4.cpp" +#include "LatestCompiledAssets/BinaryData5.cpp" +#include "LatestCompiledAssets/BinaryData6.cpp" +#include "LatestCompiledAssets/BinaryData7.cpp" +#include "LatestCompiledAssets/BinaryData8.cpp" +#include "LatestCompiledAssets/BinaryData9.cpp" +// NOLINTEND(bugprone-suspicious-include) +JUCE_END_IGNORE_WARNINGS_GCC_LIKE diff --git a/modules/melatonin_inspector/melatonin_inspector.h b/modules/melatonin_inspector/melatonin_inspector.h new file mode 100644 index 00000000..d2ea8e36 --- /dev/null +++ b/modules/melatonin_inspector/melatonin_inspector.h @@ -0,0 +1,461 @@ +/* +BEGIN_JUCE_MODULE_DECLARATION + +ID: melatonin_inspector +vendor: Sudara +version: 1.3.0 +name: Melatonin Inspector +description: A component inspector for JUCE, inspired by Figma, web inspector and Jim Credland's Component Debugger +license: MIT +dependencies: juce_gui_basics, juce_gui_extra +minimumCppStandard: 17 + +END_JUCE_MODULE_DECLARATION +*/ +#pragma once + +#ifndef PERFETTO + #define TRACE_COMPONENT(...) + #define TRACE_EVENT(category, ...) + #define TRACE_EVENT_BEGIN(category, ...) + #define TRACE_EVENT_END(category) +#endif + +#include "melatonin/lookandfeel.h" +#include "melatonin_inspector/melatonin/components/overlay.h" +#include "melatonin_inspector/melatonin/helpers/inspector_settings.h" +#include "melatonin_inspector/melatonin/helpers/overlay_mouse_listener.h" +#include "melatonin_inspector/melatonin/inspector_component.h" +#include + +namespace melatonin +{ + class Inspector : public juce::ComponentListener, public juce::DocumentWindow, private juce::Timer, public juce::FocusChangeListener + { + public: + class InspectorKeyCommands : public juce::KeyListener + { + public: + explicit InspectorKeyCommands (Inspector& i) : inspector (i) {} + Inspector& inspector; + + bool keyPressed (const juce::KeyPress& keyPress, Component* /*originatingComponent*/) override + { +#if JUCE_WINDOWS + bool modifierPresent = juce::ModifierKeys::getCurrentModifiers().isCtrlDown(); +#else + bool modifierPresent = juce::ModifierKeys::getCurrentModifiers().isCommandDown(); +#endif + if (keyPress.isKeyCurrentlyDown ('I') && modifierPresent) + { + inspector.toggle(); + return true; + } + + if (keyPress.isKeyCode (juce::KeyPress::escapeKey)) + { + if (inspector.inspectorEnabled) + { + inspector.clearSelections(); + } + return true; + } + + // let the keypress propagate + return false; + } + }; + explicit Inspector (juce::Component& rootComponent, bool inspectorEnabledAtStart = true) + : juce::DocumentWindow ("Melatonin Inspector", colors::background, 7, true) + { + TRACE_COMPONENT(); + this->addKeyListener (&keyListener); + + setRoot (rootComponent); + + // needs to come before the LNF + restoreBoundsIfNeeded(); + + // use our own lnf for both overlay and inspector + setLookAndFeel (&inspectorLookAndFeel); + overlay.setLookAndFeel (&inspectorLookAndFeel); + inspectorComponent.setLookAndFeel (&inspectorLookAndFeel); + + setUsingNativeTitleBar (true); + setContentNonOwned (&inspectorComponent, true); + setupCallbacks(); + + setResizable (true, false); // calls resized + + // order mattecrs, set the mode before we toggle on + setSelectionMode (static_cast (settings->props->getIntValue ("inspectorSelectionMode", FOLLOWS_MOUSE))); + + toggle (inspectorEnabledAtStart); + } + + ~Inspector() override + { + clearRoot(); + + this->removeKeyListener (&keyListener); + + // the mouse listener is owned and removes itself on destruction + if (selectionMode == FOLLOWS_FOCUS) + juce::Desktop::getInstance().removeFocusChangeListener (this); + + // needed, otherwise removing look and feel will save bounds + settings->props.reset(); + setLookAndFeel (nullptr); + } + + void setRoot (juce::Component& rootComponent) + { + clearRoot(); + + root = &rootComponent; + + root->addChildComponent (overlay); + overlay.setBounds (root->getLocalBounds()); + root->addComponentListener (this); + + // allow us to open/close the inspector by key command + // bit sketchy because we're modifying the source app to accept key focus + root->addKeyListener (&keyListener); + root->setWantsKeyboardFocus (true); + + fpsMeter.setRoot (*root); + overlayMouseListener.setRoot (*root); + inspectorComponent.setRoot (*root); + } + + void clearRoot() + { + if (root == nullptr) + return; + + root->removeKeyListener (&keyListener); + root->removeComponentListener (this); + + fpsMeter.clearRoot(); + overlayMouseListener.clearRoot(); + inspectorComponent.clearRoot(); + } + + void moved() override + { + TRACE_COMPONENT(); + DocumentWindow::resized(); + saveBounds(); + } + + void resized() override + { + TRACE_COMPONENT(); + DocumentWindow::resized(); + saveBounds(); + } + + // this is a bit brittle and called a bit too frequently + // for example 4-5 times on construction + void saveBounds() + { + TRACE_COMPONENT(); + if (settings->props == nullptr) + return; + + settings->props->setValue ("x", getX()); + settings->props->setValue ("y", getY()); + + // only overwrite width/height when inspectorEnabled. + // the disabled dimensions are fixed, + // so this lets us "open back up" when re-enabling + if (inspectorEnabled) + { + settings->props->setValue ("inspectorEnabledWidth", getWidth()); + settings->props->setValue ("inspectorEnabledHeight", getHeight()); + } + + settings->saveIfNeeded(); + } + + void restoreBoundsIfNeeded() + { + TRACE_COMPONENT(); + // disabled is a fixed 380x400 + // inspectorEnabled must be at least 700x800 + auto minWidth = inspectorEnabled ? 700 : 380; + auto minHeight = inspectorEnabled ? 800 : 400; + + auto x = settings->props->getIntValue ("x", 50); + auto y = settings->props->getIntValue ("y", 50); + + if (inspectorEnabled) + { + auto width = settings->props->getIntValue ("inspectorEnabledWidth", minWidth); + auto height = settings->props->getIntValue ("inspectorEnabledHeight", minHeight); + + // Note: Ideally we'd call setResizable but it recreates the desktop window + // which adds >30ms in Debug with little change of behavior + // setResizable (true, false); + + setResizeLimits (minWidth, minHeight, 1200, 1200); + setBounds (x, y, width, height); + } + else + { + // decrease the resize limits first for the setSize call to work! + // the order of these calls matters a lot + setResizeLimits (minWidth, minHeight, minWidth, minHeight); + setBounds (x, y, minWidth, minHeight); + + // Keep this commented out, as it will recreate the desktop window + // setResizable (false, false); + } + + inspectorComponent.setBounds (getLocalBounds()); + } + + void outlineComponent (Component* c) + { + TRACE_COMPONENT(); + + // don't dogfood the overlay + if (!inspectorEnabled || overlay.isParentOf (c)) + return; + + overlay.outlineComponent (c); + inspectorComponent.displayComponentInfo (c, true); + } + + void outlineDistanceCallback (Component* c) + { + TRACE_COMPONENT(); + + if (!inspectorEnabled || overlay.isParentOf (c)) + return; + + overlay.outlineDistanceCallback (c); + } + + void selectComponent (Component* c, bool shouldCollapseTree = false) + { + TRACE_COMPONENT(); + + if (!inspectorEnabled || overlay.isParentOf (c)) + return; + + overlay.selectComponent (c); + inspectorComponent.selectComponent (c, shouldCollapseTree); + } + + void dragComponent (Component* c, const juce::MouseEvent& e) + { + TRACE_COMPONENT(); + + if (!inspectorEnabled || overlay.isParentOf (c)) + return; + + overlay.dragSelectedComponent (e); + inspectorComponent.displayComponentInfo (c); + } + + void startDragComponent (Component* c, const juce::MouseEvent& e) + { + TRACE_COMPONENT(); + + if (!inspectorEnabled || overlay.isParentOf (c)) + return; + + overlay.startDraggingComponent (e); + } + + void clearSelections() + { + TRACE_COMPONENT(); + + inspectorComponent.deselectComponent(); + overlay.outlineComponent (nullptr); + overlay.selectComponent (nullptr); + } + + // closing the window means turning off the inspector + void closeButtonPressed() override + { + if (onClose) + { + // you can provide a callback to destroy the inspector + onClose(); + } + else + { + // otherwise we'll just hide it + toggle (false); + setVisible (false); + } + } + + void toggle (bool newStatus) + { + TRACE_COMPONENT(); + + // the DocumentWindow always stays open, even when disabled + setVisible (true); + + inspectorEnabled = newStatus; + inspectorComponent.toggle (newStatus); + restoreBoundsIfNeeded(); + + overlay.setVisible (newStatus); + if (newStatus) + { + if (selectionMode == FOLLOWS_MOUSE) + // without this, target apps that have UI to open the inspector + // will select that piece of UI within the same click, see #70 + callAfterDelay (500, [&] { overlayMouseListener.enable(); }); + } + else + { + clearSelections(); + overlayMouseListener.disable(); + } + } + + void toggle() + { + toggle (!inspectorEnabled); + } + + void setRootFollowsComponentUnderMouse (bool follow) + { + rootFollowsComponentUnderMouse = follow; + + if (rootFollowsComponentUnderMouse) + startTimerHz (25); + else + stopTimer(); + } + + std::function onClose; + + enum SelectionMode { + FOLLOWS_MOUSE, + FOLLOWS_FOCUS + } selectionMode { FOLLOWS_MOUSE }; + + void setSelectionMode (SelectionMode newMode) + { + if (newMode == selectionMode) + return; + + // Out with the old + switch (selectionMode) + { + case FOLLOWS_FOCUS: + selectComponent (nullptr); + juce::Desktop::getInstance().removeFocusChangeListener (this); + break; + case FOLLOWS_MOUSE: + overlayMouseListener.disable(); + break; + } + + // And in with the new + selectionMode = newMode; + switch (selectionMode) + { + case FOLLOWS_FOCUS: + juce::Desktop::getInstance().addFocusChangeListener (this); + break; + case FOLLOWS_MOUSE: + overlayMouseListener.enable(); + break; + } + + settings->props->setValue ("inspectorSelectionMode", selectionMode); + settings->saveIfNeeded(); + } + + private: + juce::SharedResourcePointer settings; + InspectorLookAndFeel inspectorLookAndFeel; + InspectorComponent inspectorComponent; + juce::Component::SafePointer root; + bool inspectorEnabled = false; + Overlay overlay; + FPSMeter fpsMeter; + OverlayMouseListener overlayMouseListener; + InspectorKeyCommands keyListener { *this }; + bool rootFollowsComponentUnderMouse = false; + + // Resize our overlay when the root component changes + void componentMovedOrResized (Component& rootComponent, bool wasMoved, bool wasResized) override + { + if (wasResized || wasMoved) + { + overlay.setBounds (rootComponent.getLocalBounds()); + } + } + + void componentBeingDeleted (juce::Component& component) override + { + if (&component == root) + { + clearRoot(); + + auto& d = juce::Desktop::getInstance(); + for (int i = 0; i < d.getNumComponents(); i++) + { + if (auto c = d.getComponent (i); c != nullptr && c != this) + { + setRoot (*c); + return; + } + } + } + } + + void globalFocusChanged (Component* focusedComponent) override + { + // ignore nullptr focus events, they are frequent and cause glitchiness + if (focusedComponent == nullptr) + return; + + // This gets sent all components, even subcomponents of the inspector itself + // (which is undesirable since we're not dogfoodiing). + if (focusedComponent->getPeer() == root->getPeer()) + selectComponent (focusedComponent); + } + + void timerCallback() override + { + for (auto& ms : juce::Desktop::getInstance().getMouseSources()) + if (auto c = ms.getComponentUnderMouse()) + if (auto top = c->getTopLevelComponent(); top != nullptr && top != root && top != this) + setRoot (*top); + } + + void setupCallbacks() + { + overlayMouseListener.outlineComponentCallback = [this] (Component* c) { this->outlineComponent (c); }; + overlayMouseListener.outlineDistanceCallback = [this] (Component* c) { this->outlineDistanceCallback (c); }; + overlayMouseListener.selectComponentCallback = [this] (Component* c) { this->selectComponent (c, true); }; + overlayMouseListener.componentStartDraggingCallback = [this] (Component* c, const juce::MouseEvent& e) { this->startDragComponent (c, e); }; + overlayMouseListener.componentDraggedCallback = [this] (Component* c, const juce::MouseEvent& e) { this->dragComponent (c, e); }; + overlayMouseListener.mouseExitCallback = [this] { if (this->inspectorEnabled) inspectorComponent.redisplaySelectedComponent(); }; + + inspectorComponent.selectComponentCallback = [this] (Component* c) { this->selectComponent (c, false); }; + inspectorComponent.outlineComponentCallback = [this] (Component* c) { this->outlineComponent (c); }; + inspectorComponent.toggleCallback = [this] (bool enable) { this->toggle (enable); }; + inspectorComponent.toggleOverlayCallback = [this] (bool enable) { + this->overlay.setVisible (enable); + overlayMouseListener.enable(); + }; + inspectorComponent.toggleFPSCallback = [this] (bool enable) { + if (enable) + this->fpsMeter.setBounds (root->getLocalBounds().removeFromRight (60).removeFromTop (40)); + this->fpsMeter.setVisible (enable); + }; + inspectorComponent.toggleSelectionMode = [this] (bool enable) { this->setSelectionMode (enable ? FOLLOWS_FOCUS : FOLLOWS_MOUSE); }; + } + }; +} diff --git a/modules/melatonin_inspector/tests/member_disabled.cpp b/modules/melatonin_inspector/tests/member_disabled.cpp new file mode 100644 index 00000000..dd48001e --- /dev/null +++ b/modules/melatonin_inspector/tests/member_disabled.cpp @@ -0,0 +1,53 @@ +#include + +//============================================================================== +class DummyApp : public juce::JUCEApplication +{ +public: + void initialise (const juce::String&) override + { + mainWindow.reset (new MainWindow (getApplicationName())); + // on CI, we start this as a background process + juce::Process::makeForegroundProcess(); + mainWindow->toFront(true); + } + + void shutdown() override + { + mainWindow = nullptr; + } + + const juce::String getApplicationName() override { return "dummy"; } + const juce::String getApplicationVersion() override { return "v1"; } + bool moreThanOneInstanceAllowed() override { return true; } + void systemRequestedQuit() override { quit(); } + + void anotherInstanceStarted (const juce::String&) override {} + + class MainWindow : public juce::DocumentWindow + { + public: + MainWindow (juce::String name) : DocumentWindow (name, + juce::Colours::lightgrey, + DocumentWindow::allButtons) + { + centreWithSize (300, 200); + setVisible (true); + inspector.setVisible(true); + } + + void closeButtonPressed() override + { + juce::JUCEApplication::getInstance()->systemRequestedQuit(); + } + + private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) + melatonin::Inspector inspector { *this, false }; + }; + +private: + std::unique_ptr mainWindow; +}; + +START_JUCE_APPLICATION (DummyApp) diff --git a/modules/melatonin_inspector/tests/member_enabled.cpp b/modules/melatonin_inspector/tests/member_enabled.cpp new file mode 100644 index 00000000..8e51d8b4 --- /dev/null +++ b/modules/melatonin_inspector/tests/member_enabled.cpp @@ -0,0 +1,54 @@ +#include + +//============================================================================== +class DummyApp : public juce::JUCEApplication +{ +public: + void initialise (const juce::String&) override + { + mainWindow.reset (new MainWindow (getApplicationName())); + // on CI, we start this as a background process + juce::Process::makeForegroundProcess(); + mainWindow->toFront(true); + } + + void shutdown() override + { + mainWindow = nullptr; + } + + const juce::String getApplicationName() override { return "dummy"; } + const juce::String getApplicationVersion() override { return "v1"; } + bool moreThanOneInstanceAllowed() override { return true; } + void systemRequestedQuit() override { quit(); } + + void anotherInstanceStarted (const juce::String&) override {} + + class MainWindow : public juce::DocumentWindow + { + public: + MainWindow (juce::String name) : DocumentWindow (name, + juce::Colours::lightgrey, + DocumentWindow::allButtons) + { + centreWithSize (300, 200); + setVisible (true); + inspector.setVisible(true); + } + + void closeButtonPressed() override + { + juce::JUCEApplication::getInstance()->systemRequestedQuit(); + } + + private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) + melatonin::Inspector inspector { *this, true }; + + }; + +private: + std::unique_ptr mainWindow; +}; + +START_JUCE_APPLICATION (DummyApp) diff --git a/modules/melatonin_inspector/tests/unique_ptr_disabled.cpp b/modules/melatonin_inspector/tests/unique_ptr_disabled.cpp new file mode 100644 index 00000000..6f499d35 --- /dev/null +++ b/modules/melatonin_inspector/tests/unique_ptr_disabled.cpp @@ -0,0 +1,55 @@ +#include + +//============================================================================== +class DummyApp : public juce::JUCEApplication +{ +public: + void initialise (const juce::String&) override + { + mainWindow.reset (new MainWindow (getApplicationName())); + inspector = std::make_unique (*mainWindow, false); + inspector->setVisible (true); + + // on CI, we start this as a background process + juce::Process::makeForegroundProcess(); + mainWindow->toFront(true); + } + + void shutdown() override + { + mainWindow = nullptr; + } + + const juce::String getApplicationName() override { return "dummy"; } + const juce::String getApplicationVersion() override { return "v1"; } + bool moreThanOneInstanceAllowed() override { return true; } + void systemRequestedQuit() override { quit(); } + + void anotherInstanceStarted (const juce::String&) override {} + + class MainWindow : public juce::DocumentWindow + { + public: + MainWindow (juce::String name) : DocumentWindow (name, + juce::Colours::lightgrey, + DocumentWindow::allButtons) + { + centreWithSize (300, 200); + setVisible (true); + } + + void closeButtonPressed() override + { + juce::JUCEApplication::getInstance()->systemRequestedQuit(); + } + + private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) + }; + +private: + std::unique_ptr mainWindow; + std::unique_ptr inspector; +}; + +START_JUCE_APPLICATION (DummyApp) diff --git a/modules/melatonin_inspector/tests/unique_ptr_enabled.cpp b/modules/melatonin_inspector/tests/unique_ptr_enabled.cpp new file mode 100644 index 00000000..dc902ff0 --- /dev/null +++ b/modules/melatonin_inspector/tests/unique_ptr_enabled.cpp @@ -0,0 +1,55 @@ +#include + +//============================================================================== +class DummyApp : public juce::JUCEApplication +{ +public: + void initialise (const juce::String&) override + { + mainWindow.reset (new MainWindow (getApplicationName())); + inspector = std::make_unique (*mainWindow); + inspector->setVisible (true); + + // on CI, we start this as a background process + juce::Process::makeForegroundProcess(); + mainWindow->toFront(true); + } + + void shutdown() override + { + mainWindow = nullptr; + } + + const juce::String getApplicationName() override { return "dummy"; } + const juce::String getApplicationVersion() override { return "v1"; } + bool moreThanOneInstanceAllowed() override { return true; } + void systemRequestedQuit() override { quit(); } + + void anotherInstanceStarted (const juce::String&) override {} + + class MainWindow : public juce::DocumentWindow + { + public: + MainWindow (juce::String name) : DocumentWindow (name, + juce::Colours::lightgrey, + DocumentWindow::allButtons) + { + centreWithSize (300, 200); + setVisible (true); + } + + void closeButtonPressed() override + { + juce::JUCEApplication::getInstance()->systemRequestedQuit(); + } + + private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow) + }; + +private: + std::unique_ptr mainWindow; + std::unique_ptr inspector; +}; + +START_JUCE_APPLICATION (DummyApp) diff --git a/modules/melatonin_inspector/update_juce_colours.rb b/modules/melatonin_inspector/update_juce_colours.rb new file mode 100755 index 00000000..ffed3b91 --- /dev/null +++ b/modules/melatonin_inspector/update_juce_colours.rb @@ -0,0 +1,49 @@ +#!/usr/bin/env ruby + +require 'find' +require 'fileutils' + +path = '../../JUCE/modules/juce_gui_basics/' +output_path = 'melatonin/helpers/colour_ids.h' +FileUtils.mkdir_p('melatonin/helpers') +output_file = File.open(output_path, 'w') + +script_name = File.basename($0) +current_date = Time.now.strftime("%Y-%m-%d") + +output_file.write("// Generated by #{script_name} on #{current_date}\n") +output_file.write("// This file collects ColourIds from the JUCE GUI module by parsing the C++ files.\n") +output_file.write("#pragma once\n") +output_file.write("#include \n\n") +output_file.write("namespace melatonin::colors\n{\n\n") + +output_file.write(" struct ColourIdMapping\n {\n int value;\n const char* name;\n };\n\n") + +output_file.write(" constexpr ColourIdMapping colourIdNames[] =\n {\n") + +Find.find(path) do |file| + if File.file?(file) + inside_enum = false + file_name = File.basename(file) + File.readlines(file).each_with_index do |line, line_number| + if line.include?('enum ColourIds') + inside_enum = true + elsif inside_enum && line.strip.start_with?('}') + inside_enum = false + elsif inside_enum && line.include?('=') + name, rest = line.strip.split('=') + value, comment_part = rest.split(' ', 2) + value.strip! + value.chomp!(',') # Remove trailing comma if present + comment = comment_part.to_s.split('*/').first.to_s.gsub('/**< ', '').strip + cpp_map = " { #{value}, \"#{name.strip}\" }, // #{file_name}:#{line_number + 1} #{comment}\n" + output_file.write(cpp_map) + end + end + end +end + +output_file.write(" };\n\n} // namespace melatonin::colors\n") +output_file.close + +puts "File written to #{output_path}" diff --git a/packaging/EULA b/packaging/EULA new file mode 100644 index 00000000..b7225975 --- /dev/null +++ b/packaging/EULA @@ -0,0 +1,19 @@ +This is a End User License Agreement ("EULA"). + +Please read it carefully before installing this software. By installing and using this software, you agree you have read this EULA and agree to the terms and conditions. If you disagree, do not install. + +1. This software is licensed for use solely by the person who paid for the license, referred to as "Licensee". + +2. Licensee can install this software on multiple computers, but only if they personally own and manage them. +Licensee is allowed to make personal backup copies of the software's materials. + +3. Licensee is prohibited from copying, altering, translating, selling, distributing, or reverse engineering the software. +Trying to access its source code, unless allowed by law, is also forbidden. + +4. Licensee must not change or hide any copyright, trademark, or other ownership notices in the software. + +5. Licensee may not share, publish or distribute the software without prior consent from Pitch Innovations. + +6. This software is provided "as-is" without any warranties, either expressed or implied, including but not limited to implied warranties of merchantability or fitness for a particular purpose. + +7. In no event shall the software's creators or distributors be liable for any damages, including but not limited to, direct, indirect, special, incidental, or consequential damages or other losses arising out of the use of or inability to use the software. diff --git a/packaging/background.jpg b/packaging/background.jpg new file mode 100644 index 00000000..cd481850 Binary files /dev/null and b/packaging/background.jpg differ diff --git a/packaging/background@2x.jpg b/packaging/background@2x.jpg new file mode 100644 index 00000000..01961bf0 Binary files /dev/null and b/packaging/background@2x.jpg differ diff --git a/packaging/dmg.json b/packaging/dmg.json new file mode 100644 index 00000000..62af8fa1 --- /dev/null +++ b/packaging/dmg.json @@ -0,0 +1,24 @@ +{ + "title": "Pamplejuce", + "icon": "pamplejuce.icns", + "background": "background.jpg", + "window": { + "position": { + "x": 100, + "y": 100 + }, + "size": { + "width": 730, + "height": 520 + } + }, + "format": "UDZO", + "contents": [ + { "x": 250, "y": 200, "type": "file", "path": "dmg/Pamplejuce Demo.app" }, + { "x": 480, "y": 200, "type": "link", "path": "/Applications" }, + { "x": 250, "y": 300, "type": "file", "path": "dmg/Pamplejuce Demo.component" }, + { "x": 480, "y": 300, "type": "file", "path": "dmg/Your Mac's Component Folder" }, + { "x": 250, "y": 400, "type": "file", "path": "dmg/Pamplejuce Demo.vst3" }, + { "x": 480, "y": 400, "type": "file", "path": "dmg/Your Mac's VST3 Folder" } + ] +} diff --git a/packaging/icon.png b/packaging/icon.png new file mode 100644 index 00000000..c6c09f9a Binary files /dev/null and b/packaging/icon.png differ diff --git a/packaging/installer.iss b/packaging/installer.iss new file mode 100644 index 00000000..95f27a9d --- /dev/null +++ b/packaging/installer.iss @@ -0,0 +1,48 @@ +#define Version Trim(FileRead(FileOpen("..\VERSION"))) +#define ProjectName GetEnv('PROJECT_NAME') +#define ProductName GetEnv('PRODUCT_NAME') +#define Publisher GetEnv('COMPANY_NAME') +#define Year GetDateTimeString("yyyy","","") + +; 'Types': What get displayed during the setup +[Types] +Name: "full"; Description: "Full installation" +Name: "custom"; Description: "Custom installation"; Flags: iscustom + +; Components are used inside the script and can be composed of a set of 'Types' +[Components] +Name: "standalone"; Description: "Standalone application"; Types: full custom +Name: "vst3"; Description: "VST3 plugin"; Types: full custom + +[Setup] +ArchitecturesInstallIn64BitMode=x64 +ArchitecturesAllowed=x64 +AppName={#ProductName} +OutputBaseFilename={#ProductName}-{#Version}-Windows +AppCopyright=Copyright (C) {#Year} {#Publisher} +AppPublisher={#Publisher} +AppVersion={#Version} +DefaultDirName="{commoncf64}\VST3\{#ProductName}.vst3" +DisableDirPage=yes + +; MAKE SURE YOU READ THE FOLLOWING! +LicenseFile="EULA" +UninstallFilesDir="{commonappdata}\{#ProductName}\uninstall" + +[UninstallDelete] +Type: filesandordirs; Name: "{commoncf64}\VST3\{#ProductName}Data" + +; MSVC adds a .ilk when building the plugin. Let's not include that. +[Files] +Source: "..\Builds\{#ProjectName}_artefacts\Release\VST3\{#ProductName}.vst3\*"; DestDir: "{commoncf64}\VST3\{#ProductName}.vst3\"; Excludes: *.ilk; Flags: ignoreversion recursesubdirs; Components: vst3 +Source: "..\Builds\{#ProjectName}_artefacts\Release\Standalone\{#ProductName}.exe"; DestDir: "{commonpf64}\{#Publisher}\{#ProductName}"; Flags: ignoreversion; Components: standalone + +[Icons] +Name: "{autoprograms}\{#ProductName}"; Filename: "{commonpf64}\{#Publisher}\{#ProductName}\{#ProductName}.exe"; Components: standalone +Name: "{autoprograms}\Uninstall {#ProductName}"; Filename: "{uninstallexe}" + +[Run] +Filename: "{cmd}"; \ + WorkingDir: "{commoncf64}\VST3"; \ + Parameters: "/C mklink /D ""{commoncf64}\VST3\{#ProductName}Data"" ""{commonappdata}\{#ProductName}"""; \ + Flags: runascurrentuser; Components: vst3 diff --git a/packaging/pamplejuce.icns b/packaging/pamplejuce.icns new file mode 100644 index 00000000..2e69b31d Binary files /dev/null and b/packaging/pamplejuce.icns differ diff --git a/test/CatchMain.cpp b/test/CatchMain.cpp deleted file mode 100644 index 121f8aa0..00000000 --- a/test/CatchMain.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#define CATCH_CONFIG_RUNNER - -#include -#include - -int main(int argc, char *argv[]) { - auto gui = juce::ScopedJuceInitialiser_GUI{}; - return Catch::Session().run(argc, argv); -} diff --git a/test/PluginTest.cpp b/test/PluginTest.cpp deleted file mode 100644 index 9694e905..00000000 --- a/test/PluginTest.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include - -TEST_CASE("simple test") -{ - CHECK(1 + 1 == 2); -} -