Merge pull request #413 from Unity-UI-Extensions/feature/V2.3.0

Feature/v2.3.0
pull/429/head
Simon (Darkside) Jackson 2023-02-07 14:06:08 +00:00 committed by GitHub
commit 8cbd1c3c1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
113 changed files with 4682 additions and 1797 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1 @@
* SimonDarksideJ

6
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,6 @@
# These are supported funding model platforms
github: [SimonDarksideJ]
ko_fi: Ko-fi.com/uiextensions
issuehunt: simondarksidej
custom: https://paypal.me/unityuiextensions

44
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,44 @@
---
name: Bug report
about: Create a report to identify a potential issue
title: 'BUG: '
labels: bug
assignees: ''
---
# Unity UI Extensions Bug Report
## Describe the bug
<!-- Please provide a clear and concise description of what the bug is. -->
## To Reproduce
<!-- Include Steps to reproduce the behavior: -->
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
## Expected behavior
<!-- A clear and concise description of what you expected to happen. -->
## Actual behavior
<!-- What is actually happening -->
## Screenshots
<!-- If applicable, add screenshots to help explain your problem. -->
## Your Setup
<!-- please complete any/all the following information. -->
- **Operation System:**
- [ ] Windows
- [ ] MacOs
- [ ] Linux
- [ ] Other <!-- Please Specify Platform -->
- **Unity Version:** <!-- Please Specify Version --> 2020.3.15f
- **Unity UI Extensions Version**
- [ ] 2.3.0 <!-- Please Specify Version or commit sha -->
## Additional context
<!-- Add any other context about the problem here. -->

View File

@ -0,0 +1,30 @@
---
name: Feature request
about: Suggest an idea for the Unity UI Extensions
title: 'Feature Request: '
labels: enhancement
assignees: ''
---
# Unity UI Extensions Feature Request
## Is your feature request related to a problem? Please describe
<!--Please provide a clear and concise description of what the problem is.
E.g. I'm always frustrated when [...]-->
## How would you classify your suggestion
<!--What type of enhancement is it, e.g:-->
- New platform support
- Usability / Configuration
- Architecture / Services
## Describe the solution you'd like
<!--A clear and concise description of what you want to happen.-->
## Describe alternatives you've considered
<!--A clear and concise description of any alternative solutions or features you've considered.-->
## Additional context
<!--Add any other context or screenshots about the feature request here.-->

View File

@ -0,0 +1,30 @@
---
name: Request for Information
about: Not sure how to do something, just ask.
title: 'RFI: '
labels: question
assignees: ''
---
# Unity UI Extensions Request for Information
<!-- As imperfect beings, we try to teach and show how to make using the framework easy to handle, but sometimes we will fall short. Help us to better educate adopters by pointing out where we need to give more information. -->
## What are you trying to achieve?
<!--
Add a clear and concise description of what is it you are trying to implement.
Include screenshots or examples from other projects if it helps.
-->
## What have you already tried
<!--
Have you tried to complete the task yourself but couldn't figure it out, if so what and why?
-->
## What material have you referenced that didn't answer your question
<!--
What other content have you tried reading / viewing that didn't answer your question?
-->

30
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,30 @@
# Unity UI Extensions - Pull Request
## Overview
<!-- Please provide a clear and concise description of the pull request. -->
## Changes
<!-- Brief list of the targeted features that are being changed. -->
- Fixes: <!--issue number or url-->
## Breaking Changes
<!-- Are there any breaking changes included in this change that would prevent or cause issue for existing projects? -->
- Breaks
## Related Submodule Changes
<!-- Include any submodule related Pull Request links here -->
- URL
## Testing status
<!-- Remove the options that do not apply -->
* No tests have been added.
* Includes unit tests.
* Includes performance tests.
* Includes integration tests.
### Manual testing status
<!-- Describe how you tested your implementation/fix. Try to mention all the cases and flows. -->
<!-- It will help the reviewer to understand if you missed any cases or test steps as well as will tell more about your feature or fix. -->

37
.github/workflows/buildupmpackages.yml vendored Normal file
View File

@ -0,0 +1,37 @@
name: Build and test UPM packages for platforms on all available Unity Versions
on:
pull_request:
branches-ignore:
- 'release'
# Ignore PRs targetting main
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
jobs:
# Run Unity build unit tests defined in the package for a single version for feature branches
Run-Partial-Unit-Tests:
name: Run Unity Unit Tests
if: github.ref != 'refs/heads/development'
uses: ./.github/workflows/rununitysinglebuild.yml
with:
unityversion: 2020.3
# Run Unity multi-version build unit tests defined in the package for the development branch
Run-Full-Unit-Tests:
name: Run Unity Unit Tests
if: github.ref == 'refs/heads/development'
uses: ./.github/workflows/rununitybuildmultiversion.yml
# Update the package release version
Update-Version:
name: Update Package Version
uses: ./.github/workflows/tagrelease.yml
with:
build-target: windows
secrets: inherit

View File

@ -0,0 +1,459 @@
name: Run Unity Builds
on:
workflow_call:
inputs:
dependencies:
description: "json array of dependencies and their targets"
required: false
type: string
jobs:
run_build:
name: Run Unity Build process
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: windows
unityVersion: 2019.4
build-target: Android
- os: macos
unityVersion: 2019.4
build-target: iOS
- os: windows
unityVersion: 2019.4
build-target: StandaloneWindows64
- os: windows
unityVersion: 2019.4
build-target: WSAPlayer
- os: windows
unityVersion: 2020.3
build-target: Android
- os: macos
unityVersion: 2020.3
build-target: iOS
- os: windows
unityVersion: 2020.3
build-target: StandaloneWindows64
- os: windows
unityVersion: 2020.3
build-target: WSAPlayer
- os: windows
unityVersion: 2021.3
build-target: Android
- os: macos
unityVersion: 2021.3
build-target: iOS
- os: windows
unityVersion: 2021.3
build-target: StandaloneWindows64
- os: windows
unityVersion: 2021.3
build-target: WSAPlayer
- os: windows
unityVersion: 2022.2
build-target: Android
- os: macos
unityVersion: 2022.2
build-target: iOS
- os: windows
unityVersion: 2022.2
build-target: StandaloneWindows64
- os: windows
unityVersion: 2022.2
build-target: WSAPlayer
steps:
- name: Script Version
run: |
echo "::group::Script Versioning"
$scriptVersion = "1.0.0"
echo "Build Script Version: $scriptVersion"
echo "::endgroup::"
shell: pwsh
- uses: actions/checkout@v3
with:
submodules: recursive
clean: true
- id: build
name: 'Run Unity Builds'
run: |
echo "::group::Set Hub and editor locations"
$unityVersion = ${{ matrix.unityVersion }}
echo "::group::Set Hub and editor locations"
## Set Hub and editor locations
if ( (-not $global:PSVersionTable.Platform) -or ($global:PSVersionTable.Platform -eq "Win32NT") )
{
$hubPath = "C:\Program Files\Unity Hub\Unity Hub.exe"
$editorRootPath = "C:\Program Files\Unity\Hub\Editor\"
$editorFileEx = "\Editor\Unity.exe"
#"Unity Hub.exe" -- --headless help
#. 'C:\Program Files\Unity Hub\Unity Hub.exe' -- --headless help
function unity-hub
{
& $hubPath -- --headless $args.Split(" ") | Out-String -NoNewline
}
}
elseif ( $global:PSVersionTable.OS.Contains("Darwin") )
{
$hubPath = "/Applications/Unity Hub.app/Contents/MacOS/Unity Hub"
$editorRootPath = "/Applications/Unity/Hub/Editor/"
$editorFileEx = "/Unity.app/Contents/MacOS/Unity"
# /Applications/Unity\ Hub.app/Contents/MacOS/Unity\ Hub -- --headless help
function unity-hub
{
& $hubPath -- --headless $args.Split(" ") | Out-String -NoNewline
}
}
elseif ( $global:PSVersionTable.OS.Contains("Linux") )
{
$hubPath = "$HOME/Unity Hub/UnityHub.AppImage"
$editorRootPath = "$HOME/Unity/Hub/Editor/"
$editorFileEx = "/Editor/Unity"
# /UnityHub.AppImage --headless help
# xvfb-run --auto-servernum "$HOME/Unity Hub/UnityHub.AppImage" --headless help
function unity-hub
{
xvfb-run --auto-servernum "$hubPath" --headless $args.Split(" ")
}
}
echo "::endgroup::"
echo "::group::Get String function to query a string for a value"
function GetString($InputString, $InputPattern, $MatchIndex)
{
$regExResult = $InputString | Select-String -Pattern $InputPattern
if($regExResult.Length -gt 0)
{
return $regExResult.Matches[$MatchIndex].Value
}
else
{
return 0
}
}
function Get-LetterCount
{
Param ([string]$string)
return $string.Length
}
echo "::endgroup::"
echo "::group::Get Installed Unity version based on Matrix"
echo 'Script Start'
echo "Unity hub path is {$hubPath}"
echo "Requested unity version is {$unityVersion}"
$InstalledUnityVersions = unity-hub editors
$editorRootPath = unity-hub ip -g
echo "Installed unity versions are {$InstalledUnityVersions}"
echo "Unity install path is {$editorRootPath}"
$versionLength = Get-LetterCount $unityVersion
if ($versionLength -eq 4) {
$queryUnityVersion = GetString $InstalledUnityVersions "$unityVersion.{4,7}" -MatchIndex 0
}
elseif ($versionLength -eq 6) {
$queryUnityVersion = GetString $InstalledUnityVersions "$unityVersion.{4,5}" -MatchIndex 0
}
else {
$queryUnityVersion = GetString $InstalledUnityVersions "$unityVersion" -MatchIndex 0
}
echo "Found unity version is {$queryUnityVersion}"
if ($queryUnityVersion -ne 0)
{
$unityVersion = $queryUnityVersion.Trim(","," ").Trim()
echo "Long Unity version is $unityVersion"
}
else
{
echo "Unity $unityVersion not found on this machine, skipping"
exit 0
}
echo "::endgroup::"
echo "::group::Search for Editor if not found"
$checkPath = Join-Path $editorRootPath $unityVersion
echo "Testing for editor at $checkPath"
while (-not (Test-Path "$checkPath")) {
$source = $unityVersion.Replace("f1","")
$newversion = $source.Split(".")
$newversion[2] = [int]$newversion[2] - 1
if ([int]$newversion[2] -lt 1) {
echo "Unity ${{ matrix.unityVersion }} not found on this machine, skipping"
exit 0
}
$newunityVersion = "{0}.{1}.{2}f1" -f $newversion[0],$newversion[1],$newversion[2]
echo "Unity $unityVersion not found on this machine, trying $newunityVersion"
$unityVersion = $newunityVersion
$checkPath = Join-Path $editorRootPath $unityVersion
}
echo "::endgroup::"
echo "::group::Set editor locations"
if ( (-not $global:PSVersionTable.Platform) -or ($global:PSVersionTable.Platform -eq "Win32NT") ) {
echo 'Building using Windows'
$editorFileEx = "/Editor/Unity.exe"
$editorrunpath = Join-Path $editorRootPath $unityVersion $editorFileEx
function unity-editor {
#$p = Start-Process -Verbose -NoNewWindow -PassThru -Wait -FilePath "$editorrunpath" -ArgumentList (@(' -batchmode') + $args.Split(" "))
& $editorrunpath -batchmode $args.Split(" ") | Out-String
}
}
elseif ( $global:PSVersionTable.OS.Contains("Darwin") ) {
echo 'Building using Mac'
$editorFileEx = "/Unity.app/Contents/MacOS/Unity"
$editorrunpath = Join-Path $editorRootPath $unityVersion $editorFileEx
function unity-editor {
#$p = Start-Process -Verbose -NoNewWindow -PassThru -Wait -FilePath "$editorrunpath" -ArgumentList (@(' -batchmode') + $args.Split(" "))
& $editorrunpath -batchmode $args.Split(" ") | Out-String
}
}
elseif ( $global:PSVersionTable.OS.Contains("Linux") ) {
echo 'Building using Linux'
$editorFileEx = "/Editor/Unity"
$editorrunpath = Join-Path $editorRootPath $unityVersion $editorFileEx
function unity-editor {
xvfb-run --auto-servernum "$editorrunpath" -batchmode $args.Split(" ")
}
}
else
{
echo 'Unknown build platform'
}
echo "::endgroup::"
echo "::group::Test Unity version is installed"
if ( -not (Test-Path "$editorrunpath") )
{
Write-Error "Editor not Found for $unityVersion"
exit 1
}
else {
echo "Editor Path is {$editorrunpath}"
}
echo "::endgroup::"
echo "::group::Setup logging and run Unit tests"
# Log detail
$logDirectory = "Logs"
if (Test-Path -Path $logDirectory) {
echo "Clearing logs from a previous run"
Remove-item $logDirectory -recurse
}
$logDirectory = New-Item -ItemType Directory -Force -Path $logDirectory | Select-Object
echo "Log Directory: $logDirectory"
$date = Get-Date -Format "yyyyMMddTHHmmss"
# If run manually, the Refname is actually blank, so just use date
if([string]::IsNullOrEmpty(${GITHUB_REF_NAME})) {
$logName = "$logDirectory$directorySeparatorChar$date"
}
else{
$logName = "$logDirectory$directorySeparatorChar${GITHUB_REF_NAME}-$date"
}
$logPath = "$logName.log"
$testsLogPath = "$logName-tests.xml"
echo "Logpath [$logPath]"
echo "TestsPath [$testsLogPath]"
echo "::endgroup::"
echo "::group::Grouping Package in a UPM folder"
$UPMFolderName = 'u'
if ( -not (Test-Path '$UPMFolderName') )
{
New-Item $UPMFolderName -ItemType Directory
}
Move-Item -Path * -Destination $UPMFolderName -exclude $UPMFolderName
echo "::endgroup::"
echo "::group::Creating Temp Unity project"
$TempUnityProjectName = 'P'
unity-editor '-createProject' $TempUnityProjectName -quit
$destinationPath = $TempUnityProjectName + $directorySeparatorChar + 'packages'
Move-Item -Path $UPMFolderName -Destination $destinationPath
echo "::endgroup::"
echo "::group::If required, clone dependencies in to test project"
<# Dependency option requires specific inputs
* A dependency input string in json format, listing each dependency by name and git url, e.g.
$dependencies = '[{"ASADependencies": "github.com/SimonDarksideJ/upmGithubActionsTests.git"}]'
*Note, remove the https:// portion to allow using a PAT to access the repo
The Name of the dependency should ALSO MATCH the name of the branch on the repo where the dependency is held (files intended for the packages folder)
* Additionally, if Manifest entries are required, then a manifest file with those dependencies (and ONLY the new dependancies) should also be in the dependency branch named the same as the branch name
e.g. "ASADependencies.json" - keep the same structure, but only the dependancy entries
!!Does NOT support additional scoped registries at this time! #>
echo "---------------------------------------------"
echo "Read dependancy input value"
if([string]::IsNullOrEmpty('${{ inputs.dependencies }}'))
{
echo "No dependencies provided"
echo "input ${{ inputs.dependencies }}"
echo "------------------------------"
}
else {
echo "dependencies provided, validating"
# Read dependancy input value
$dependencies = '${{ inputs.dependencies }}'
if([string]::IsNullOrEmpty('${{ secrets.GIT_USER_NAME }}') -or [string]::IsNullOrEmpty('${{ secrets.GIT_PAT }}')){
echo ""
echo "Secrets for GIT_USER_NAME or GIT_PAT missing, please register them with access to this runner"
echo "*Note, Organisation secrets are not accessible to Forked repos and need registering in the local fork"
exit 1
}
echo "---------------------------------------------"
echo "Read dependancy input values as json"
$JSONdependencies = $dependencies | ConvertFrom-Json
echo $JSONdependencies
# Read current Manifest json
$manifestPath = $destinationPath + $directorySeparatorChar + 'manifest.json'
$projectManifest = Get-Content -Path $manifestPath | ConvertFrom-Json
$strArray = $projectManifest.dependencies.PsObject.Properties | ForEach-Object {"$($_.Name)@$($_.Value),"}
echo "---------------------------------------------"
echo "Loop through new dependancies and add them to the project Manifest"
foreach($dependency in $JSONdependencies){
$dependency.PsObject.Properties | ForEach-Object -Process {
$dependencyName = $_.Name
$dependencyPath = $dependencyName.Replace("/","_")
$dependencyURL = $_.Value
echo "---------------------------------------------"
echo "Cloning dependency - Name [$dependencyName] - Path [$dependencyPath] - URL [$dependencyURL]"
$cloneURL = "https://${{ secrets.GIT_USER_NAME }}:${{ secrets.GIT_PAT }}@$dependencyURL"
echo "cloning $dependencyName from $dependencyURL and moving to $destinationPath"
echo "git path - $cloneURL"
# Clone Dependancy repo to destination folder
git clone -b $dependencyName --single-branch $cloneURL $dependencyPath
if( -not (Test-Path -Path "$dependencyPath")){
echo "Unable to clone $dependencyName from $dependencyURL"
exit 1
}
# Move files from clone path into packages folder, if the dependency contains a UPM package then move the entire folder and not just its contents
if (Test-Path -Path "$dependencyPath/package.json") {
$package_json = Get-Content -Path $dependencyPath/package.json | ConvertFrom-Json
$packageName = $package_json.name
Rename-Item $dependencyPath $packageName
echo "Moving whole $packageName UPM package to $destinationPath"
Move-Item -Path "$packageName" -Destination $destinationPath
}
else {
echo "Moving the contents of $dependencyName into the $destinationPath folder"
Move-Item -Path "$dependencyPath/*" -Destination $destinationPath
}
# Get Dependency manifest entries (if applicable)
if (Test-Path -Path "$destinationPath/$dependencyName.json") {
$dependencyManifest = Get-Content -Path "$destinationPath/$dependencyName.json" | ConvertFrom-Json
$dependencyManifest.dependencies.PsObject.Properties | ForEach-Object {
$strArray += "$($_.Name)@$($_.Value),"
}
}
else{
echo "No denendency json found called $destinationPath/$dependencyName.json, skipping adding additional manifest entries"
}
}
}
echo "---------------------------------------------"
# Reformat combined dependancies list
$strArray = $strArray.Trim(",") | ConvertTo-Json
$strArray = $strArray.Replace("@",'":"').Replace("[","{").Replace("]","}")
$strArrayObject = $strArray | ConvertFrom-Json
# Save manifest back to project
$projectManifest.dependencies = $strArrayObject
$projectManifest | ConvertTo-Json | Set-Content -Path "$destinationPath/manifest.json"
echo "Project updated with the following dependencies"
echo "-----------------------------------------------"
echo $projectManifest
}
echo "::endgroup::"
echo "::group::Run build"
echo "---------------------------------------------"
echo "Start Testing"
echo "Unity Command\n[unity-editor -projectPath $TempUnityProjectName -logfile $logPath -batchmode -nographics -quit -buildTarget ${{ matrix.build-target }}]"
unity-editor -projectPath $TempUnityProjectName -logfile $logPath -batchmode -nographics -quit -buildTarget ${{ matrix.build-target }}
echo "---------------------------------------------"
echo "::group::Unity Unit tests Results"
if (Test-Path $testsLogPath) {
echo "Test Run results for ${GITHUB_REPOSITORY} Branch ${GITHUB_REF}"
echo ""
Select-Xml -Path $testsLogPath -XPath '/test-run/test-suite' | ForEach-Object { "Name: " +$_.Node.name, ", Result: " + $_.Node.result, ", Total Tests: " + $_.Node.total, ", Passed: " + $_.Node.passed, ", Failed: " + $_.Node.failed, ", Skipped: " + $_.Node.skipped }
}
else {
echo "No test results found for ${GITHUB_REPOSITORY} Branch ${GITHUB_REF} at $testsLogPath"
echo ""
}
echo "::endgroup::"
if($LASTEXITCODE -ne '0'){
echo "::group::Unity Unit tests errors"
$exitCode = $testResult.ExitCode
Get-Content $logPath
echo "Build failed due to errors ($LASTEXITCODE), please check the log at $logPath"
echo "::endgroup::"
exit $LASTEXITCODE
}
echo "::endgroup::"
shell: pwsh
- uses: actions/upload-artifact@v3
if: always()
with:
name: unity-build-log
path: Logs/**

View File

@ -0,0 +1,424 @@
name: Run Limited Unity Builds
on:
workflow_call:
inputs:
unityVersion:
description: "The version of Unity to validate on"
required: true
type: string
dependencies:
description: "json array of dependencies and their targets"
required: false
type: string
jobs:
run_build:
name: Run Unity Build process
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: macos
build-target: iOS
- os: windows
build-target: Android
- os: windows
build-target: StandaloneWindows64
- os: windows
build-target: WSAPlayer
steps:
- name: Script Version
run: |
echo "::group::Script Versioning"
$scriptVersion = "1.0.0"
echo "Build Script Version: $scriptVersion"
echo "::endgroup::"
shell: pwsh
- uses: actions/checkout@v3
with:
submodules: recursive
clean: true
- id: build
name: 'Run Unity Builds'
run: |
echo "::group::Set Hub and editor locations"
$unityVersion = ${{ inputs.unityVersion }}
echo "::group::Set Hub and editor locations"
## Set Hub and editor locations
if ( (-not $global:PSVersionTable.Platform) -or ($global:PSVersionTable.Platform -eq "Win32NT") )
{
$hubPath = "C:\Program Files\Unity Hub\Unity Hub.exe"
$editorRootPath = "C:\Program Files\Unity\Hub\Editor\"
$editorFileEx = "\Editor\Unity.exe"
#"Unity Hub.exe" -- --headless help
#. 'C:\Program Files\Unity Hub\Unity Hub.exe' -- --headless help
function unity-hub
{
& $hubPath -- --headless $args.Split(" ") | Out-String -NoNewline
}
}
elseif ( $global:PSVersionTable.OS.Contains("Darwin") )
{
$hubPath = "/Applications/Unity Hub.app/Contents/MacOS/Unity Hub"
$editorRootPath = "/Applications/Unity/Hub/Editor/"
$editorFileEx = "/Unity.app/Contents/MacOS/Unity"
# /Applications/Unity\ Hub.app/Contents/MacOS/Unity\ Hub -- --headless help
function unity-hub
{
& $hubPath -- --headless $args.Split(" ") | Out-String -NoNewline
}
}
elseif ( $global:PSVersionTable.OS.Contains("Linux") )
{
$hubPath = "$HOME/Unity Hub/UnityHub.AppImage"
$editorRootPath = "$HOME/Unity/Hub/Editor/"
$editorFileEx = "/Editor/Unity"
# /UnityHub.AppImage --headless help
# xvfb-run --auto-servernum "$HOME/Unity Hub/UnityHub.AppImage" --headless help
function unity-hub
{
xvfb-run --auto-servernum "$hubPath" --headless $args.Split(" ")
}
}
echo "::endgroup::"
echo "::group::Get String function to query a string for a value"
function GetString($InputString, $InputPattern, $MatchIndex)
{
$regExResult = $InputString | Select-String -Pattern $InputPattern
if($regExResult.Length -gt 0)
{
return $regExResult.Matches[$MatchIndex].Value
}
else
{
return 0
}
}
function Get-LetterCount
{
Param ([string]$string)
return $string.Length
}
echo "::endgroup::"
echo "::group::Get Installed Unity version based on Matrix"
echo 'Script Start'
echo "Unity hub path is {$hubPath}"
echo "Requested unity version is {$unityVersion}"
$InstalledUnityVersions = unity-hub editors
$editorRootPath = unity-hub ip -g
echo "Installed unity versions are {$InstalledUnityVersions}"
echo "Unity install path is {$editorRootPath}"
$versionLength = Get-LetterCount $unityVersion
if ($versionLength -eq 4) {
$queryUnityVersion = GetString $InstalledUnityVersions "$unityVersion.{4,7}" -MatchIndex 0
}
elseif ($versionLength -eq 6) {
$queryUnityVersion = GetString $InstalledUnityVersions "$unityVersion.{4,5}" -MatchIndex 0
}
else {
$queryUnityVersion = GetString $InstalledUnityVersions "$unityVersion" -MatchIndex 0
}
echo "Found unity version is {$queryUnityVersion}"
if ($queryUnityVersion -ne 0)
{
$unityVersion = $queryUnityVersion.Trim(","," ").Trim()
echo "Long Unity version is $unityVersion"
}
else
{
echo "Unity $unityVersion not found on this machine, skipping"
exit 0
}
echo "::endgroup::"
echo "::group::Search for Editor if not found"
$checkPath = Join-Path $editorRootPath $unityVersion
echo "Testing for editor at $checkPath"
while (-not (Test-Path "$checkPath")) {
$source = $unityVersion.Replace("f1","")
$newversion = $source.Split(".")
$newversion[2] = [int]$newversion[2] - 1
if ([int]$newversion[2] -lt 1) {
echo "Unity ${{ inputs.unityVersion }} not found on this machine, skipping"
exit 0
}
$newunityVersion = "{0}.{1}.{2}f1" -f $newversion[0],$newversion[1],$newversion[2]
echo "Unity $unityVersion not found on this machine, trying $newunityVersion"
$unityVersion = $newunityVersion
$checkPath = Join-Path $editorRootPath $unityVersion
}
echo "::endgroup::"
echo "::group::Set editor locations"
if ( (-not $global:PSVersionTable.Platform) -or ($global:PSVersionTable.Platform -eq "Win32NT") ) {
echo 'Building using Windows'
$editorFileEx = "/Editor/Unity.exe"
$editorrunpath = Join-Path $editorRootPath $unityVersion $editorFileEx
function unity-editor {
#$p = Start-Process -Verbose -NoNewWindow -PassThru -Wait -FilePath "$editorrunpath" -ArgumentList (@(' -batchmode') + $args.Split(" "))
& $editorrunpath -batchmode $args.Split(" ") | Out-String
}
}
elseif ( $global:PSVersionTable.OS.Contains("Darwin") ) {
echo 'Building using Mac'
$editorFileEx = "/Unity.app/Contents/MacOS/Unity"
$editorrunpath = Join-Path $editorRootPath $unityVersion $editorFileEx
function unity-editor {
#$p = Start-Process -Verbose -NoNewWindow -PassThru -Wait -FilePath "$editorrunpath" -ArgumentList (@(' -batchmode') + $args.Split(" "))
& $editorrunpath -batchmode $args.Split(" ") | Out-String
}
}
elseif ( $global:PSVersionTable.OS.Contains("Linux") ) {
echo 'Building using Linux'
$editorFileEx = "/Editor/Unity"
$editorrunpath = Join-Path $editorRootPath $unityVersion $editorFileEx
function unity-editor {
xvfb-run --auto-servernum "$editorrunpath" -batchmode $args.Split(" ")
}
}
else
{
echo 'Unknown build platform'
}
echo "::endgroup::"
echo "::group::Test Unity version is installed"
if ( -not (Test-Path "$editorrunpath") )
{
Write-Error "Editor not Found for $unityVersion"
exit 1
}
else {
echo "Editor Path is {$editorrunpath}"
}
echo "::endgroup::"
echo "::group::Setup logging and run Unit tests"
# Log detail
$logDirectory = "Logs"
if (Test-Path -Path $logDirectory) {
echo "Clearing logs from a previous run"
Remove-item $logDirectory -recurse
}
$logDirectory = New-Item -ItemType Directory -Force -Path $logDirectory | Select-Object
echo "Log Directory: $logDirectory"
$date = Get-Date -Format "yyyyMMddTHHmmss"
# If run manually, the Refname is actually blank, so just use date
if([string]::IsNullOrEmpty(${GITHUB_REF_NAME})) {
$logName = "$logDirectory$directorySeparatorChar$date"
}
else{
$logName = "$logDirectory$directorySeparatorChar${GITHUB_REF_NAME}-$date"
}
$logPath = "$logName.log"
$testsLogPath = "$logName-tests.xml"
echo "Logpath [$logPath]"
echo "TestsPath [$testsLogPath]"
echo "::endgroup::"
echo "::group::Grouping Package in a UPM folder"
$UPMFolderName = 'u'
if ( -not (Test-Path '$UPMFolderName') )
{
New-Item $UPMFolderName -ItemType Directory
}
Move-Item -Path * -Destination $UPMFolderName -exclude $UPMFolderName
echo "::endgroup::"
echo "::group::Creating Temp Unity project"
$TempUnityProjectName = 'P'
unity-editor '-createProject' $TempUnityProjectName -quit
$destinationPath = $TempUnityProjectName + $directorySeparatorChar + 'packages'
Move-Item -Path $UPMFolderName -Destination $destinationPath
echo "::endgroup::"
echo "::group::If required, clone dependencies in to test project"
<# Dependency option requires specific inputs
* A dependency input string in json format, listing each dependency by name and git url, e.g.
$dependencies = '[{"ASADependencies": "github.com/SimonDarksideJ/upmGithubActionsTests.git"}]'
*Note, remove the https:// portion to allow using a PAT to access the repo
The Name of the dependency should ALSO MATCH the name of the branch on the repo where the dependency is held (files intended for the packages folder)
* Additionally, if Manifest entries are required, then a manifest file with those dependencies (and ONLY the new dependancies) should also be in the dependency branch named the same as the branch name
e.g. "ASADependencies.json" - keep the same structure, but only the dependancy entries
!!Does NOT support additional scoped registries at this time! #>
echo "---------------------------------------------"
echo "Read dependancy input value"
if([string]::IsNullOrEmpty('${{ inputs.dependencies }}'))
{
echo "No dependencies provided"
echo "input ${{ inputs.dependencies }}"
echo "------------------------------"
}
else {
echo "dependencies provided, validating"
# Read dependancy input value
$dependencies = '${{ inputs.dependencies }}'
if([string]::IsNullOrEmpty('${{ secrets.GIT_USER_NAME }}') -or [string]::IsNullOrEmpty('${{ secrets.GIT_PAT }}')){
echo ""
echo "Secrets for GIT_USER_NAME or GIT_PAT missing, please register them with access to this runner"
echo "*Note, Organisation secrets are not accessible to Forked repos and need registering in the local fork"
exit 1
}
echo "---------------------------------------------"
echo "Read dependancy input values as json"
$JSONdependencies = $dependencies | ConvertFrom-Json
echo $JSONdependencies
# Read current Manifest json
$manifestPath = $destinationPath + $directorySeparatorChar + 'manifest.json'
$projectManifest = Get-Content -Path $manifestPath | ConvertFrom-Json
$strArray = $projectManifest.dependencies.PsObject.Properties | ForEach-Object {"$($_.Name)@$($_.Value),"}
echo "---------------------------------------------"
echo "Loop through new dependancies and add them to the project Manifest"
foreach($dependency in $JSONdependencies){
$dependency.PsObject.Properties | ForEach-Object -Process {
$dependencyName = $_.Name
$dependencyPath = $dependencyName.Replace("/","_")
$dependencyURL = $_.Value
echo "---------------------------------------------"
echo "Cloning dependency - Name [$dependencyName] - Path [$dependencyPath] - URL [$dependencyURL]"
$cloneURL = "https://${{ secrets.GIT_USER_NAME }}:${{ secrets.GIT_PAT }}@$dependencyURL"
echo "cloning $dependencyName from $dependencyURL and moving to $destinationPath"
echo "git path - $cloneURL"
# Clone Dependancy repo to destination folder
git clone -b $dependencyName --single-branch $cloneURL $dependencyPath
if( -not (Test-Path -Path "$dependencyPath")){
echo "Unable to clone $dependencyName from $dependencyURL"
exit 1
}
# Move files from clone path into packages folder, if the dependency contains a UPM package then move the entire folder and not just its contents
if (Test-Path -Path "$dependencyPath/package.json") {
$package_json = Get-Content -Path $dependencyPath/package.json | ConvertFrom-Json
$packageName = $package_json.name
Rename-Item $dependencyPath $packageName
echo "Moving whole $packageName UPM package to $destinationPath"
Move-Item -Path "$packageName" -Destination $destinationPath
}
else {
echo "Moving the contents of $dependencyName into the $destinationPath folder"
Move-Item -Path "$dependencyPath/*" -Destination $destinationPath
}
# Get Dependency manifest entries (if applicable)
if (Test-Path -Path "$destinationPath/$dependencyName.json") {
$dependencyManifest = Get-Content -Path "$destinationPath/$dependencyName.json" | ConvertFrom-Json
$dependencyManifest.dependencies.PsObject.Properties | ForEach-Object {
$strArray += "$($_.Name)@$($_.Value),"
}
}
else{
echo "No denendency json found called $destinationPath/$dependencyName.json, skipping adding additional manifest entries"
}
}
}
echo "---------------------------------------------"
# Reformat combined dependancies list
$strArray = $strArray.Trim(",") | ConvertTo-Json
$strArray = $strArray.Replace("@",'":"').Replace("[","{").Replace("]","}")
$strArrayObject = $strArray | ConvertFrom-Json
# Save manifest back to project
$projectManifest.dependencies = $strArrayObject
$projectManifest | ConvertTo-Json | Set-Content -Path "$destinationPath/manifest.json"
echo "Project updated with the following dependencies"
echo "-----------------------------------------------"
echo $projectManifest
}
echo "::endgroup::"
echo "::group::Run build"
echo "---------------------------------------------"
echo "Start Testing"
echo "Unity Command\n[unity-editor -projectPath $TempUnityProjectName -logfile $logPath -batchmode -nographics -quit -buildTarget ${{ matrix.build-target }}]"
unity-editor -projectPath $TempUnityProjectName -logfile $logPath -batchmode -nographics -quit -buildTarget ${{ matrix.build-target }}
echo "---------------------------------------------"
echo "::group::Unity Unit tests Results"
if (Test-Path $testsLogPath) {
echo "Test Run results for ${GITHUB_REPOSITORY} Branch ${GITHUB_REF}"
echo ""
Select-Xml -Path $testsLogPath -XPath '/test-run/test-suite' | ForEach-Object { "Name: " +$_.Node.name, ", Result: " + $_.Node.result, ", Total Tests: " + $_.Node.total, ", Passed: " + $_.Node.passed, ", Failed: " + $_.Node.failed, ", Skipped: " + $_.Node.skipped }
}
else {
echo "No test results found for ${GITHUB_REPOSITORY} Branch ${GITHUB_REF} at $testsLogPath"
echo ""
}
echo "::endgroup::"
if($LASTEXITCODE -ne '0'){
echo "::group::Unity Unit tests errors"
$exitCode = $testResult.ExitCode
Get-Content $logPath
echo "Build failed due to errors ($LASTEXITCODE), please check the log at $logPath"
echo "::endgroup::"
exit $LASTEXITCODE
}
echo "::endgroup::"
shell: pwsh
- uses: actions/upload-artifact@v3
if: always()
with:
name: unity-build-log
path: Logs/**

146
.github/workflows/tagrelease.yml vendored Normal file
View File

@ -0,0 +1,146 @@
name: Package UPM project and deploy
on:
workflow_call:
inputs:
build-target:
required: true
type: string
build-type:
required: false
default: 'pre-release'
type: string
# options:
# - major
# - minor
# - patch
# - pre-release
# - build
outputs:
packageversion:
description: "Returns the version of Unity the UPM package requires"
value: ${{ jobs.packageRelease.outputs.packageversion }}
secrets:
GIT_USER_NAME:
required: false
jobs:
packageRelease:
name: Package UPM Project and tag
runs-on: ${{ inputs.build-target }}
outputs:
packageversion: ${{ steps.getpackageversion.outputs.packageversion }}
steps:
- name: Script Version
run: |
echo "::group::Script Versioning"
$scriptVersion = "1.0.2"
echo "Build Script Version: $scriptVersion"
echo "::endgroup::"
shell: pwsh
- uses: actions/checkout@v3
with:
submodules: recursive
clean: true
token: ${{ secrets.GIT_PAT }}
- uses: actions/setup-node@v3
- name: Set Github vars
run: |
if([string]::IsNullOrEmpty('${{ secrets.GIT_USER_NAME }}')){
if([string]::IsNullOrEmpty('${{ secrets.GIT_USER_NAME }}')){
$gitUser = "action"
$gitEmail = "action@github.com"
}
else {
$gitUser = "${GITHUB_ACTOR}"
$gitEmail = "$gitUser@users.noreply.github.com"
}
}
else {
$gitUser = "${{ secrets.GIT_USER_NAME }}"
$gitEmail = "$gitUser@users.noreply.github.com"
}
echo "email $gitUser@users.noreply.github.com"
git config --global user.email "$gitUser@users.noreply.github.com"
git config --global user.name "$gitUser"
shell: pwsh
- id: getpackageversion
name: Bump UPM Package version
run: |
function UpdateProjectVersionJSON {
param (
[Parameter(Mandatory)]
$type,
$packageFile = 'package.json'
)
<#
Type of build can be one of the following
- 'build' # Build release - 1.0.0-pre-release.0+1
- 'pre-release' # Pre-Release release - 1.0.0-pre-release.1
- 'patch' # Patch release - 1.0.1
- 'minor' # Minor release - 1.1.0
- 'major' # Major release - 2.0.0
#>
if ( -not (Test-Path -Path $packageFile) ) {
Write-Error "Failed to find a valid project manifest at `"$packageFile`""
return $null
}
$packageInfo = (Get-Content $packageFile -Raw) | ConvertFrom-Json
Write-Host "Detected Project Version:" $packageInfo.version
function IfNull($a, $b, $c) { if ($null -eq $a) { return $b } else { return $c } }
$packageSemVer = [System.Management.Automation.SemanticVersion]$packageInfo.version
$majorVersion = if($null -eq $packageSemVer.Major) {0} else {$packageSemVer.Major}
$minorVersion = if($null -eq $packageSemVer.Minor) {0} else {$packageSemVer.Minor}
$patchVersion = if($null -eq $packageSemVer.Patch) {0} else {$packageSemVer.Patch}
$prereleaseVersion = if($null -eq $packageSemVer.PreReleaseLabel) {0} else {$packageSemVer.PreReleaseLabel.Replace('pre-release.','')}
$buildVersion = if($null -eq $packageSemVer.BuildLabel) {0} else {$packageSemVer.BuildLabel}
# work out new version
switch ($type) {
'build' {
[int]$buildVersion += 1
$newPackageSemVer = [System.Management.Automation.SemanticVersion]::New($majorVersion, $minorVersion, $patchVersion, "pre-release." + $prereleaseVersion, $buildVersion)
}
'pre-release' {
[int]$prereleaseVersion += 1
$newPackageSemVer = [System.Management.Automation.SemanticVersion]::New($majorVersion, $minorVersion, $patchVersion, "pre-release." + $prereleaseVersion)
}
'patch' {
[int]$patchVersion += 1
$newPackageSemVer = [System.Management.Automation.SemanticVersion]::New($majorVersion, $minorVersion, $patchVersion)
}
'minor' {
[int]$minorVersion += 1
$newPackageSemVer = [System.Management.Automation.SemanticVersion]::New($majorVersion, $minorVersion, 0)
}
'major' {
[int]$majorVersion += 1
$newPackageSemVer = [System.Management.Automation.SemanticVersion]::New($majorVersion, 0, 0)
}
}
Write-Host "Upgrading project version [$packageSemVer] to [$newPackageSemVer]"
# Write out updated package info
$packageInfo.version = $newPackageSemVer.ToString()
$packageInfo | ConvertTo-Json | Set-Content $packageFile
return $packageInfo.version
}
$packageFile = 'package.json'
$result = UpdateProjectVersionJSON("${{ inputs.build-type }}","$packageFile")
if([string]::IsNullOrEmpty($result)) {
echo "Version patch failed"
exit 1
}
echo "packageversion=$result" >> $env:GITHUB_OUTPUT
git add "$packageFile"
git commit -m "Auto increment pre-release version to $result [skip ci]"
git tag -fa "$result" "${GITHUB_SHA}" -m "$result Release"
git push origin --force
shell: pwsh

3
.gitmodules vendored
View File

@ -1,4 +1,3 @@
[submodule "Examples~"] [submodule "Examples~"]
path = Examples~ path = Examples~
url = https://SimonDarksideJ@bitbucket.org/UnityUIExtensions/unity-ui-extensions.git url = https://github.com/Unity-UI-Extensions/com.unity.uiextensions-examples.git
branch = Examples

View File

@ -35,6 +35,4 @@ sysinfo.txt
/.vs /.vs
*.gitmodules *.gitmodules
# NPM publish exclusions **/.github/*
bitbucket-pipelines.yml
bitbucket-pipelines.yml.meta

View File

@ -4,74 +4,117 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/). The format is based on [Keep a Changelog](http://keepachangelog.com/).
## 2019.6 - 2.5 - Bug squash - 2021/05/10 ## Release 2.3 - Reanimation - 2022/02/07
Its been a while since the last update and although Unity keeps changing, thankfully the parts underneath do not. THanks to some awesome work by our contributors and the test teams, we made a run on some underlying bugs and issues. If you spot anything else, please log it on the BitBucket site for resolution. It has been a tough time for all since the last update, but things have been moving steadily along. In the past few months there has been a concerted effort to revamp and update the project ready for Unity 2022, as well as migrating the source repository over to GitHub and refreshing all the things.
We hope the new release is better for everyone and we have paid close attention to the editor menus and places to find all the controls for this release.
> Be sure to logon to the new [Gitter Chat](https://gitter.im/Unity-UI-Extensions/Lobby) site for the UI Extensions project, if you have any questions, queries or suggestions To get up to speed with the Unity UI Extensions, check out the [Getting Started](https://unity-ui-extensions.github.io/GettingStarted.html) Page.
> Ways to get in touch:
>
> - [Gitter Chat](https://gitter.im/Unity-UI-Extensions/Lobby) site for the UI Extensions project
> - [GitHub Discussions](https://github.com/Unity-UI-Extensions/com.unity.uiextensions/discussions), if you have any questions, queries or suggestions
> >
> Much easier that posting a question / issue on YouTube, Twitter or Facebook :D > Much easier that posting a question / issue on YouTube, Twitter or Facebook :D
>
> ## [UIExtensions Gitter Channel](https://gitter.im/Unity-UI-Extensions/Lobby)
## Breaking changes
### Added For customers upgrading from earlier versions of Unity to Unity 2020, please be aware of the Breaking change related to Text Based components. You will need to manually replace any UI using the older ```Text``` component and replace them with ```TextMeshPro``` versions. This is unavoidable due to Unity deprecating the Text component.
Nothing new this time, bugfix release. > New users to 2022 are unaffected as all the Editor commands have been updated to use the newer TextMeshPro versions.
### Changed For more details, see the [deprecation notice](https://github.com/Unity-UI-Extensions/com.unity.uiextensions/discussions/428) on GitHub.
- Updated UI Line connector to use relative position instead of anchored position to verify if the Lines need updating. ## Added
- Allow menu prefabs to not have to have canvas components. This allows you to use any type of prefab as a "menu". Adam Kapos mentions the concept on the Unite talk, https://youtu.be/wbmjturGbAQ?t=1654
- Updated segment line drawing for Line Lists. Seems Unity no longer needs UV's to be wrapped manually.
- Updated the AutoCompleteComboBox to display text as entered (instead of all lowercase)
- Updated the ComboBox to display text as entered (instead of all lowercase)
- Updated ComboBox Examples to include programmatic versions
- Further ComboBox improvements including:
* Upwards panel
* Start fixes
* Item Template resize
* Disabled sorting on combobox as it wasn't working
* Disabled Slider handle when not in use
* Updated Example
- Updated the new Input system switch and tested against 2021
### Deprecated - Added new FIFO based UI Line Render when dynamic line rendering is needed.
- Added ResetSelectableHighlight component.
- Added SetArc method to UICircle as requested.
- Added new UIHorizontalScroller based on UIVerticalScroller.
- Added OnHighlightChanged and OnPressChanged events for UI Button.
- Added error handling around setting Unity UI Components for Vertical/Horizontal ScrollSnaps.
- Added Editor Menu Option to create a Min/Max slider.
- Added the ability to set a specific item for combobox controls on start and not just the first.
- Added the ability to disable the combo boxes and make them read-only.
None ## Changed
### Fixed - Refresh FancyScrollView with the latest fixes
- All Text based components updated to use TextMeshPro from Unity 2022 **Breaking Change**
- Reordering issue resolved with ScrollRectOcclusion. - Clean-up and reset pivots on scene start.
- Fixed Sorting at min and max positions for ScrollRect - Merged in feature/improved-ui-highlightable (pull request UILineRenderer - issues with specifying point locations at runtime #123).
- Updated ScrollToSelect script provided by zero3growlithe, tested and vastly reduces the previous jitter. Still present but barely noticeable now. - Merged in fix/rangesliderfix (pull request HorizontalScrollSnap Mask Area doesn't work when content created dynamically #125).
- Fixed Issue # 363 Update Combobox control that takes multiple items programmatically, to only allow distinct items - Merged in fix/infinitescrollcontentsize (pull request Gradient initialization should be in Awake() #126).
- Fixed the issues where dragging outside the range slider handle causes the range to update. - Resolves #369 - Merged in feature/controlTouchUp (pull request UILineRenderer mesh not updating in Editor scene view #127).
- Resolves an issue with Unity putting the previous controls vertex array in an uninitialised control. - Upgraded RangeSlider to work in both Horizontal and Vertical setups.
- Applied J.R. Mitchell's fix for the Accordion Controls/Accordion/AccordionElement.cs - resolves: #364 - Merged in RangeSlider-upgrade. (pull request Newtonsoft.Json.dll conflict #131)
- Resolved issue where the Content Scroll snap issue with only 1 child. Resolves #362 - Updated UIVertical scroller to be 2022 compliant.
- Updated the PaginationManager to override if the ScrollSnap is in motion. - Updated Curly UI to wait until end of the frame to recalculate positions.
- Updated Depth Texture sampler in UI Particles Shaders.
- Updated Points to always be an array of 1 when set to nothing for the Line Renderer.
- Updated Cooldown button to work with Keyboard input.
- Removed unneeded size calculation which caused some issues with mixed content.
- Resolved an issue whereby the last row in a flow layout group would not size correctly.
- Updated all components using "LayoutGroup" to override their OnDisable.
- Updated validation in the new MinMaxSlider.
- Updated Editor create options to add the correct Event System Input manager.
- Updated initialisation logic to not cause an endless loop in the TabNavigationHelper.
- Updated "Action" use to "UnityAction" to avoid Unity issues for DropDowns.
- Updated UIVerticalScroller for standards.
- Updated ReorderableList/ReorderableListElement to prevent creating a fake object for non-transferable items.
- Updated panel drawing for ComboBox controls and added DropdownOffset.
- Updated build issue with ReorderableListElement.
- Updated NonDrawingGraphic to require a CanvasRender, else it causes an error on run.
### Removed ## Deprecated
None - Marked ScrollPositionController as Obsolete, users should use the new Scoller.
- BestFitOutline - Deprecated in Unity 2020 onwards. (still available for earlier versions)
- NicerOutline - Deprecated in Unity 2020 onwards. (still available for earlier versions)
- Marked TileSizeFitter as obsolete as Unity has made this unworkable.
### Additional Notes ## Fixed
#### [Installation Instructions](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/GettingStarted) - Resolved issues with DisplayAbove and using a 0 ItemsToDisplay for ComboBox controls.
- Resolved startup issue that prevented the control from being used (Unity changed the start order in some instances), this was causing null reference issues with comboboxes.
- Patch fix for UILineRenderer.
- Resolves issue where the lower range value would become stuck when moving.
- Updated Infinite scroll to work with content of different sizes.
- Updated Dropdown list to NOT resize text Rect on draw.
- Revised the Curly UI fix as it was preventing the graphic from being updated in the scene view.
- Cleanup and ensuring the UIParticleSystem is disposed in onDestroy correctly.
- Clean up range slider unused variables.
As of Unity 2019, there are now two paths for getting access to the Unity UI Extensions project: ## Additional Notes
### [Installation Instructions](https://unity-ui-extensions.github.io/UPMInstallation.html)
- Unity 2019 or higher
The recommended way to add the Unity UI Extensions project to your solution is to use the Unity package Manager. Simply use the Unity Package Manager to reference the project to install it The recommended way to add the Unity UI Extensions project to your solution is to use the Unity package Manager. Simply use the Unity Package Manager to reference the project to install it
Alternatively, you can also use the pre-compiled Unity packages if you wish, however, UPM offers full versioning support to allow you to switch versions as you wish. New for 2020, we have added OpenUPM support and the package can be installed using the following [OpenUPM CLI](https://openupm.com/docs/) command:
```cli
`openupm add com.unity.uiextensions`
```
> For more details on using [OpenUPM CLI, check the docs here](https://github.com/openupm/openupm-cli#installation).
- Unity Package Manager - manual
Alternatively, you can also add the package manually through the Unity package manager using the scope ```com.unity.uiextensions```, see the [Unity Package Manager docs](https://learn.unity.com/tutorial/the-package-manager) for more information.
- Unity 2018 or lower - Unity 2018 or lower
The pre-compiled Unity assets are the only solution for Unity 2018 or earlier due to the changes in the Unity UI framework in Unity made for 2019. The pre-compiled Unity assets are the only solution for Unity 2018 or earlier due to the changes in the Unity UI framework in Unity made for 2019.
Either clone / download this repository to your machine and then copy the scripts in, or use the pre-packaged .UnityPackage for your version of Unity and import it as a custom package in to your project. Either clone / download this repository to your machine and then copy the scripts in, or use the pre-packaged .UnityPackage for your version of Unity and import it as a custom package in to your project.
#### Upgrade Notes ### Upgrade Notes
### UPM
If you are using UPM to gain access to the Unity UI Extensions, then you only need to update to the latest version in the Package Manager, no other changes needed.
### Customers using the .UnityPackage
Due to the restructure of the package to meet Unity's new package guidelines, we recommend **Deleting the current Unity UI Extensions** folder prior to importing the new package. Due to the restructure of the package to meet Unity's new package guidelines, we recommend **Deleting the current Unity UI Extensions** folder prior to importing the new package.

View File

@ -5,17 +5,20 @@
The Unity UI Extensions project is a collection of extension scripts/effects and controls to enhance your Unity UI experience. This includes over 70+ controls, utilities, effects and some much-needed love to make the most out of the Unity UI system (formally uGUI) in Unity. The Unity UI Extensions project is a collection of extension scripts/effects and controls to enhance your Unity UI experience. This includes over 70+ controls, utilities, effects and some much-needed love to make the most out of the Unity UI system (formally uGUI) in Unity.
[Check out our Tumblr page for a sneak peek](https://www.tumblr.com/blog/unityuiextensions) [Check out our Tumblr page for a sneak peek](https://www.tumblr.com/blog/unityuiextensions)
> Contact the UI Extensions Team You can follow the UI Extensions team for updates and news on:
> Be sure to logon to the new [Gitter Chat site](https://gitter.im/Unity-UI-Extensions/Lobby) for the UI Extensions project, if you have any questions, queries or suggestions
> Much easier than posting a question / issue on [YouTube](http://www.youtube.com/c/UnityUIExtensions), [Twitter](https://twitter.com/hashtag/UnityUIExtensions) or [Facebook](https://www.facebook.com/UnityUIExtensions) :D ### [Twitter - #unityuiextensions](https://twitter.com/search?q=%23unityuiextensions) / [Facebook](https://www.facebook.com/UnityUIExtensions/) / [YouTube](https://www.youtube.com/@UnityUIExtensions)
> Ways to get in touch:
> >
> [**UIExtensions Gitter Chanel**](https://gitter.im/Unity-UI-Extensions/Lobby) > - [Gitter Chat](https://gitter.im/Unity-UI-Extensions/Lobby) site for the UI Extensions project
> - [GitHub Discussions](https://github.com/Unity-UI-Extensions/com.unity.uiextensions/discussions), if you have any questions, queries or suggestions
# Installing Unity UI Extensions # Installing Unity UI Extensions
To install this package, follow the instructions in the Package Manager documentation. To install this package, follow the instructions in the Package Manager documentation.
For more details on [Getting Started](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/GettingStarted) please checkout the [online documentation here](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/GettingStarted). For more details on [Getting Started](https://unity-ui-extensions.github.io/GettingStarted) please checkout the [online documentation here](https://unity-ui-extensions.github.io/).
# Using Unity UI Extensions # Using Unity UI Extensions
@ -23,7 +26,7 @@ The UI Extensions project provides many automated functions to add the various c
Some of the features are also available through the GameObject "Add Component" menu in the inspector. Some of the features are also available through the GameObject "Add Component" menu in the inspector.
For a full list of the controls and how they are used, please see the [online documentation](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Controls) for the project. For a full list of the controls and how they are used, please see the [online documentation](https://unity-ui-extensions.github.io/Controls.html) for the project.
# Technical details # Technical details
@ -31,64 +34,102 @@ For a full list of the controls and how they are used, please see the [online do
This version of the Unity UI Extensions is compatible with the following versions of the Unity Editor: This version of the Unity UI Extensions is compatible with the following versions of the Unity Editor:
- 2019 and above - the recommended path for 2019+ is to use the Unity Package Manager to get access to the package. Full details for installing via UPM can be [found here](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/UPMInstallation). - 2019 and above - the recommended path for 2019+ is to use the Unity Package Manager to get access to the package. Full details for installing via UPM can be [found here](https://unity-ui-extensions.github.io/UPMInstallation.html).
> Alternatively, the Asset packages have been tested to work with 2019 as well if you prefer to install that way. > Alternatively, the Asset packages have been tested to work with 2019 as well if you prefer to install that way.
- 2018 and below - for 2018 and use this package, you will have to import the asset package(s), either from the Asset Store or from the alternate download locations [listed here](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Downloads). - 2018 and below - for 2018 and use this package, you will have to import the asset package(s), either from the Asset Store or from the alternate download locations [listed here](https://unity-ui-extensions.github.io/Downloads).
## [Release Notes](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/ReleaseNotes/RELEASENOTES) ## [Release Notes](https://unity-ui-extensions.github.io/ReleaseNotes/RELEASENOTES)
### 2019.5 - 2.3 - Accelerated Deployment ## Release 2.3 - Reanimation - 2022/02/07
#### Added It has been a tough time for all since the last update, but things have been moving steadily along. In the past few months there has been a concerted effort to revamp and update the project ready for Unity 2022, as well as migrating the source repository over to GitHub and refreshing all the things.
We hope the new release is better for everyone and we have paid close attention to the editor menus and places to find all the controls for this release.
- Add squircle primitive To get up to speed with the Unity UI Extensions, check out the [Getting Started](https://unity-ui-extensions.github.io/GettingStarted.html) Page.
- Adding new magnetic scroll control
- Added a static library to collate shaders on first use.
- Finalized new InputManagerHelper, which translates input based on the operating input system, new or old Updated CardStack2D to have defined keyboard input or specific gamepad input over the older axisname for new input system.
- Updated DropDown and Autocomplete controls based on feedback in #204
#### Changed > Ways to get in touch:
>
> - [Gitter Chat](https://gitter.im/Unity-UI-Extensions/Lobby) site for the UI Extensions project
> - [GitHub Discussions](https://github.com/Unity-UI-Extensions/com.unity.uiextensions/discussions), if you have any questions, queries or suggestions
>
> Much easier that posting a question / issue on YouTube, Twitter or Facebook :D
- Examples now included with UPM delivery and available as a button on the UPM package manager window ## Breaking changes
- Updated DropDown and Autocomplete controls based on feedback in #204
- Updated Accordion to support both Vertical as well as Horizontal layout
- Updated ComboBox controls to improve better programmatic controls
- Updates to the Infinite scroll to support content of various sizes
- Updated UI Knob control - enabled dragging outside the target area, added example scene
- Minor update to MagneticInfinite Scroll
- Refactored and extended the ContentScrollSnap control
- Added protection against errors and empty scrollrect content
- Added new SetNewItems function to add children programmatically to the control and reset accordingly
- Patch supplied by a contributor to improve the texture sheet use with the UIParticlesystem
- Added "SetKnobValue" function which allows the setting of Value and loops
- Added the programmatic capability to change the parent scroll rect on the ScrollConflictManager at runtime.
#### Deprecated For customers upgrading from earlier versions of Unity to Unity 2020, please be aware of the Breaking change related to Text Based components. You will need to manually replace any UI using the older ```Text``` component and replace them with ```TextMeshPro``` versions. This is unavoidable due to Unity deprecating the Text component.
None > New users to 2022 are unaffected as all the Editor commands have been updated to use the newer TextMeshPro versions.
#### Fixed For more details, see the [deprecation notice](https://github.com/Unity-UI-Extensions/com.unity.uiextensions/discussions/428) on GitHub.
- Fix to add a "RequireComponent" to Primitives as Unity 2020 does not add them by default ## Added
- Remove old Examples submodule
- Updated submodules to hide Examples folder Additionally, updated Package manifest to allow importing of examples direct from UPM package.
- Fixed hard swipe to ensure it only ever moves one page, no matter how far you swipe.
- Fixed a conflict when using the ScrollConflictManager in child content of a HSS or VSS
- Fix for UI Particle system looping
- Fixed public GoToScreen call to only raise events internally (not multiple)
- Final roll-up and fix. Resolved race condition for associated pagination controls.
- Fixed issue with page events not being raised when inertia was disabled (velocity was always zero)
- When cloned, reorderable list was creating a second List Content component that was not initialized. Refactored to ensure only one list content was present and is initialized correctly
- Reorderable list items marked as transferable, remain transferable after being dropped
- Patch to resolve issues without the new Input System installed
- Refined magnetic scroll and dependencies while documenting Updated example
- Patch Tooltip
#### Removed - Added new FIFO based UI Line Render when dynamic line rendering is needed.
- Added ResetSelectableHighlight component.
- Added SetArc method to UICircle as requested.
- Added new UIHorizontalScroller based on UIVerticalScroller.
- Added OnHighlightChanged and OnPressChanged events for UI Button.
- Added error handling around setting Unity UI Components for Vertical/Horizontal ScrollSnaps.
- Added Editor Menu Option to create a Min/Max slider.
- Added the ability to set a specific item for combobox controls on start and not just the first.
- Added the ability to disable the combo boxes and make them read-only.
None ## Changed
- Refresh FancyScrollView with the latest fixes
- All Text based components updated to use TextMeshPro from Unity 2022 **Breaking Change**
- Clean-up and reset pivots on scene start.
- Merged in feature/improved-ui-highlightable (pull request UILineRenderer - issues with specifying point locations at runtime #123).
- Merged in fix/ragesliderfix (pull request HorizontalScrollSnap Mask Area doesn't work when content created dynamically #125).
- Merged in fix/infinitescrollcontentsize (pull request Gradient initialization should be in Awake() #126).
- Merged in feature/controlTouchUp (pull request UILineRenderer mesh not updating in Editor scene view #127).
- Upgraded RangeSlider to work in both Horizontal and Vertical setups.
- Merged in RangeSlider-upgrade. (pull request Newtonsoft.Json.dll conflict #131)
- Updated UIVertical scroller to be 2022 compliant.
- Updated Curly UI to wait until end of the frame to recalculate positions.
- Updated Depth Texture sampler in UI Particles Shaders.
- Updated Points to always be an array of 1 when set to nothing for the Line Renderer.
- Updated Cooldown button to work with Keyboard input.
- Removed unneeded size calculation which caused some issues with mixed content.
- Resolved an issue whereby the last row in a flow layout group would not size correctly.
- Updated all components using "LayoutGroup" to override their OnDisable.
- Updated validation in the new MinMaxSlider.
- Updated Editor create options to add the correct Event System Input manager.
- Updated initialisation logic to not cause an endless loop in the TabNavigationHelper.
- Updated "Action" use to "UnityAction" to avoid Unity issues for DropDowns.
- Updated UIVerticalScroller for standards.
- Updated ReorderableList/ReorderableListElement to prevent creating a fake object for non-transferable items.
- Updated panel drawing for ComboBox controls and added DropdownOffset.
- Updated build issue with ReorderableListElement.
- Updated NonDrawingGraphic to require a CanvasRender, else it causes an error on run.
## Deprecated
- Marked ScrollPositionController as Obsolete, users should use the new Scoller.
- BestFitOutline - Deprecated in Unity 2020 onwards. (still available for earlier versions)
- NicerOutline - Deprecated in Unity 2020 onwards. (still available for earlier versions)
- Marked TileSizeFitter as obsolete as Unity has made this unworkable.
## Fixed
- Resolved issues with DisplayAbove and using a 0 ItemsToDisplay for ComboBox controls.
- Resolved startup issue that prevented the control from being used (Unity changed the start order in some instances), this was causing null reference issues with comboboxes.
- Patch fix for UILineRenderer.
- Resolves issue where the lower range value would become stuck when moving.
- Updated Infinite scroll to work with content of different sizes.
- Updated Dropdown list to NOT resize text Rect on draw.
- Revised the Curly UI fix as it was preventing the graphic from being updated in the scene view.
- Cleanup and ensuring the UIParticleSystem is disposed in onDestroy correctly.
- Clean up range slider unused variables.
- [UI Extensions Issue log](https://github.com/Unity-UI-Extensions/com.unity.uiextensions/issues)
## Upgrade Notes
We recommend using the UPM delivery method. If you are using the Unity asset, there should be no issues updating but if you have a problem, just deleted the old Unity-UI-Extensions folder and import the asset new.
# Document revision history # Document revision history
@ -98,3 +139,4 @@ None
|September 3rd, 2019|2019.1 (v2.1) released, First major update for the 2.0 series.| |September 3rd, 2019|2019.1 (v2.1) released, First major update for the 2.0 series.|
|August 8th, 2020|2019.4 (v2.2) released, New UPM Delivery.| |August 8th, 2020|2019.4 (v2.2) released, New UPM Delivery.|
|October 10th, 2020|2019.5 (v2.2) released, New UPM fast delivery| |October 10th, 2020|2019.5 (v2.2) released, New UPM fast delivery|
|February 7th, 2022|v2.3 released, New Home, UPM fast delivery via OpenUPM|

View File

@ -1,12 +1,11 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 8701e045b26e51f4eb345f2ccb3c13f5 guid: c1047f9974e7ee1478bbf5490a7a62d8
timeCreated: 1426804458 MonoImporter:
licenseType: Free externalObjects: {}
MonoImporter: serializedVersion: 2
serializedVersion: 2 defaultReferences: []
defaultReferences: [] executionOrder: 0
executionOrder: 0 icon: {instanceID: 0}
icon: {instanceID: 0} userData:
userData: assetBundleName:
assetBundleName: assetBundleVariant:
assetBundleVariant:

View File

@ -68,7 +68,12 @@ namespace UnityEngine.UI.Extensions
Handles.color = Color.gray; Handles.color = Color.gray;
EditorGUI.BeginChangeCheck(); EditorGUI.BeginChangeCheck();
#if UNITY_2022_1_OR_NEWER
Vector3 newCornerPos = Handles.FreeMoveHandle(script.transform.TransformPoint(cornerPos), HandleUtility.GetHandleSize(script.transform.TransformPoint(cornerPos)) / 7, Vector3.one, Handles.SphereHandleCap);
#else
Vector3 newCornerPos = Handles.FreeMoveHandle(script.transform.TransformPoint(cornerPos), script.transform.rotation, HandleUtility.GetHandleSize(script.transform.TransformPoint(cornerPos)) / 7, Vector3.one, Handles.SphereHandleCap); Vector3 newCornerPos = Handles.FreeMoveHandle(script.transform.TransformPoint(cornerPos), script.transform.rotation, HandleUtility.GetHandleSize(script.transform.TransformPoint(cornerPos)) / 7, Vector3.one, Handles.SphereHandleCap);
#endif
Handles.Label(newCornerPos, string.Format("Corner Mover")); Handles.Label(newCornerPos, string.Format("Corner Mover"));
newCornerPos = script.transform.InverseTransformPoint(newCornerPos); newCornerPos = script.transform.InverseTransformPoint(newCornerPos);

View File

@ -4,8 +4,6 @@
using UnityEditor; using UnityEditor;
using UnityEditor.AnimatedValues; using UnityEditor.AnimatedValues;
// For maintenance, every new [SerializeField] variable in Scroller must be declared here
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[CustomEditor(typeof(Scroller))] [CustomEditor(typeof(Scroller))]
@ -20,16 +18,11 @@ namespace UnityEngine.UI.Extensions
SerializedProperty inertia; SerializedProperty inertia;
SerializedProperty decelerationRate; SerializedProperty decelerationRate;
SerializedProperty snap; SerializedProperty snap;
SerializedProperty snapEnable;
SerializedProperty snapVelocityThreshold;
SerializedProperty snapDuration;
SerializedProperty snapEasing;
SerializedProperty draggable; SerializedProperty draggable;
SerializedProperty scrollbar; SerializedProperty scrollbar;
AnimBool showElasticity; AnimBool showElasticity;
AnimBool showInertiaRelatedValues; AnimBool showInertiaRelatedValues;
AnimBool showSnapEnableRelatedValues;
void OnEnable() void OnEnable()
{ {
@ -41,16 +34,11 @@ namespace UnityEngine.UI.Extensions
inertia = serializedObject.FindProperty("inertia"); inertia = serializedObject.FindProperty("inertia");
decelerationRate = serializedObject.FindProperty("decelerationRate"); decelerationRate = serializedObject.FindProperty("decelerationRate");
snap = serializedObject.FindProperty("snap"); snap = serializedObject.FindProperty("snap");
snapEnable = serializedObject.FindProperty("snap.Enable");
snapVelocityThreshold = serializedObject.FindProperty("snap.VelocityThreshold");
snapDuration = serializedObject.FindProperty("snap.Duration");
snapEasing = serializedObject.FindProperty("snap.Easing");
draggable = serializedObject.FindProperty("draggable"); draggable = serializedObject.FindProperty("draggable");
scrollbar = serializedObject.FindProperty("scrollbar"); scrollbar = serializedObject.FindProperty("scrollbar");
showElasticity = new AnimBool(Repaint); showElasticity = new AnimBool(Repaint);
showInertiaRelatedValues = new AnimBool(Repaint); showInertiaRelatedValues = new AnimBool(Repaint);
showSnapEnableRelatedValues = new AnimBool(Repaint);
SetAnimBools(true); SetAnimBools(true);
} }
@ -58,14 +46,12 @@ namespace UnityEngine.UI.Extensions
{ {
showElasticity.valueChanged.RemoveListener(Repaint); showElasticity.valueChanged.RemoveListener(Repaint);
showInertiaRelatedValues.valueChanged.RemoveListener(Repaint); showInertiaRelatedValues.valueChanged.RemoveListener(Repaint);
showSnapEnableRelatedValues.valueChanged.RemoveListener(Repaint);
} }
void SetAnimBools(bool instant) void SetAnimBools(bool instant)
{ {
SetAnimBool(showElasticity, !movementType.hasMultipleDifferentValues && movementType.enumValueIndex == (int)MovementType.Elastic, instant); SetAnimBool(showElasticity, !movementType.hasMultipleDifferentValues && movementType.enumValueIndex == (int)MovementType.Elastic, instant);
SetAnimBool(showInertiaRelatedValues, !inertia.hasMultipleDifferentValues && inertia.boolValue, instant); SetAnimBool(showInertiaRelatedValues, !inertia.hasMultipleDifferentValues && inertia.boolValue, instant);
SetAnimBool(showSnapEnableRelatedValues, !snapEnable.hasMultipleDifferentValues && snapEnable.boolValue, instant);
} }
void SetAnimBool(AnimBool a, bool value, bool instant) void SetAnimBool(AnimBool a, bool value, bool instant)
@ -126,31 +112,6 @@ namespace UnityEngine.UI.Extensions
{ {
EditorGUILayout.PropertyField(decelerationRate); EditorGUILayout.PropertyField(decelerationRate);
EditorGUILayout.PropertyField(snap); EditorGUILayout.PropertyField(snap);
using (new EditorGUI.IndentLevelScope())
{
DrawSnapRelatedValues();
}
}
}
}
void DrawSnapRelatedValues()
{
if (snap.isExpanded)
{
EditorGUILayout.PropertyField(snapEnable);
using (var group = new EditorGUILayout.FadeGroupScope(showSnapEnableRelatedValues.faded))
{
if (!group.visible)
{
return;
}
EditorGUILayout.PropertyField(snapVelocityThreshold);
EditorGUILayout.PropertyField(snapDuration);
EditorGUILayout.PropertyField(snapEasing);
} }
} }
} }

View File

@ -0,0 +1,121 @@
///Credit brogan89
///Sourced from - https://github.com/brogan89/MinMaxSlider
using System;
using UnityEditor;
using UnityEditor.UI;
namespace UnityEngine.UI.Extensions
{
[CustomEditor(typeof(MinMaxSlider), true)]
[CanEditMultipleObjects]
public class MinMaxSliderEditor : SelectableEditor
{
private SerializedProperty _customCamera;
private SerializedProperty _sliderBounds;
private SerializedProperty _minHandle;
private SerializedProperty _maxHandle;
private SerializedProperty _minText;
private SerializedProperty _maxText;
private SerializedProperty _textFormat;
private SerializedProperty _middleGraphic;
private SerializedProperty _minLimit;
private SerializedProperty _maxLimit;
private SerializedProperty _wholeNumbers;
private SerializedProperty _minValue;
private SerializedProperty _maxValue;
private SerializedProperty _onValueChanged;
private readonly GUIContent label = new GUIContent("Min Max Values");
protected override void OnEnable()
{
base.OnEnable();
_customCamera = serializedObject.FindProperty("customCamera");
_sliderBounds = serializedObject.FindProperty("sliderBounds");
_minHandle = serializedObject.FindProperty("minHandle");
_maxHandle = serializedObject.FindProperty("maxHandle");
_minText = serializedObject.FindProperty("minText");
_maxText = serializedObject.FindProperty("maxText");
_textFormat = serializedObject.FindProperty("textFormat");
_middleGraphic = serializedObject.FindProperty("middleGraphic");
_minLimit = serializedObject.FindProperty("minLimit");
_maxLimit = serializedObject.FindProperty("maxLimit");
_wholeNumbers = serializedObject.FindProperty("wholeNumbers");
_minValue = serializedObject.FindProperty("minValue");
_maxValue = serializedObject.FindProperty("maxValue");
_onValueChanged = serializedObject.FindProperty("onValueChanged");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
float minLimitOld = _minLimit.floatValue;
float maxLimitOld = _maxLimit.floatValue;
float minValueOld = _minValue.floatValue;
float maxValueOld = _maxValue.floatValue;
EditorGUILayout.PropertyField(_customCamera);
EditorGUILayout.PropertyField(_sliderBounds);
EditorGUILayout.PropertyField(_minHandle);
EditorGUILayout.PropertyField(_maxHandle);
EditorGUILayout.PropertyField(_middleGraphic);
EditorGUILayout.PropertyField(_minText);
EditorGUILayout.PropertyField(_maxText);
EditorGUILayout.PropertyField(_textFormat);
EditorGUILayout.PropertyField(_minLimit);
EditorGUILayout.PropertyField(_maxLimit);
EditorGUILayout.PropertyField(_wholeNumbers);
EditorGUILayout.PropertyField(_minValue);
EditorGUILayout.PropertyField(_maxValue);
float minValue = Mathf.Clamp(_minValue.floatValue, _minLimit.floatValue, _maxLimit.floatValue);
float maxValue = Mathf.Clamp(_maxValue.floatValue, _minLimit.floatValue, _maxLimit.floatValue);
EditorGUILayout.MinMaxSlider(label, ref minValue, ref maxValue, _minLimit.floatValue, _maxLimit.floatValue);
bool anyValueChanged = !IsEqualFloat(minValueOld, minValue)
|| !IsEqualFloat(maxValueOld, maxValue)
|| !IsEqualFloat(minLimitOld, _minLimit.floatValue)
|| !IsEqualFloat(maxLimitOld, _maxLimit.floatValue);
if (anyValueChanged)
{
MinMaxSlider slider = (MinMaxSlider)target;
// force limits to ints if whole numbers.
// needed to do this here because it wouldn't set in component script for some reason
if (slider.wholeNumbers)
{
_minLimit.floatValue = Mathf.RoundToInt(_minLimit.floatValue);
_maxLimit.floatValue = Mathf.RoundToInt(_maxLimit.floatValue);
}
// set slider values
slider.SetValues(minValue, maxValue, _minLimit.floatValue, _maxLimit.floatValue);
}
EditorGUILayout.Space();
EditorGUILayout.PropertyField(_onValueChanged);
serializedObject.ApplyModifiedProperties();
}
/// <summary>
/// Returns true if floating point numbers are within 0.01f (close enough to be considered equal)
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
private static bool IsEqualFloat(float a, float b)
{
return Math.Abs(a - b) < 0.01f;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 69352ed1561021b48ac258f81f48a988
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -3,10 +3,12 @@
// For maintenance, every new [SerializeField] variable in ScrollPositionController must be declared here // For maintenance, every new [SerializeField] variable in ScrollPositionController must be declared here
using System;
using UnityEditor; using UnityEditor;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[Obsolete("ScrollPositionController has been replaced by the Scroller component", true)]
[CustomEditor(typeof(ScrollPositionController))] [CustomEditor(typeof(ScrollPositionController))]
[CanEditMultipleObjects] [CanEditMultipleObjects]
public class ScrollPositionControllerEditor : Editor public class ScrollPositionControllerEditor : Editor

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,12 @@
{ {
"name": "UnityUIExtensions.editor", "name": "UnityUIExtensions.editor",
"rootNamespace": "",
"references": [ "references": [
"GUID:343deaaf83e0cee4ca978e7df0b80d21", "GUID:343deaaf83e0cee4ca978e7df0b80d21",
"GUID:2bafac87e7f4b9b418d9448d219b01ab", "GUID:2bafac87e7f4b9b418d9448d219b01ab",
"GUID:cf414061cae3a954baf92763590f3127" "GUID:cf414061cae3a954baf92763590f3127",
"GUID:6055be8ebefd69e48b49212b09b47b2f",
"GUID:75469ad4d38634e559750d17036d5f7c"
], ],
"includePlatforms": [ "includePlatforms": [
"Editor" "Editor"
@ -14,5 +17,6 @@
"precompiledReferences": [], "precompiledReferences": [],
"autoReferenced": true, "autoReferenced": true,
"defineConstraints": [], "defineConstraints": [],
"versionDefines": [] "versionDefines": [],
"noEngineReferences": false
} }

@ -1 +1 @@
Subproject commit d08257d62c3c95771540f51f77f50a491715d3b7 Subproject commit 774bde78bf8792ad8de1c96ad7e874932fd716d7

244
README.md
View File

@ -1,10 +1,10 @@
# Unity UI Extensions README # Unity UI Extensions README
This is an extension project for the new Unity UI system which can be found at: [Unity UI Source](https://bitbucket.org/Unity-Technologies/ui) This is an extension project for the new Unity UI system which can be found at: [Unity UI Source](https://github.com/Unity-Technologies/uGUI)
> [Check out the control demos on our Tumblr page](https://unityuiextensions.tumblr.com/) > [Check out the control demos on our Tumblr page](https://unityuiextensions.tumblr.com/)
## [Intro](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/GettingStarted) ## [Intro](https://unity-ui-extensions.github.io/GettingStarted)
For more info, here's a little introduction video for the project: For more info, here's a little introduction video for the project:
@ -12,15 +12,16 @@ For more info, here's a little introduction video for the project:
You can follow the UI Extensions team for updates and news on: You can follow the UI Extensions team for updates and news on:
### [Twitter](https://twitter.com/hashtag/UnityUIExtensions?src=hash) / [Facebook](https://www.facebook.com/UnityUIExtensions/) / [YouTube](https://www.youtube.com/channel/UCG3gZOkmL-2rmZat4ufv28Q) ### [Twitter - #unityuiextensions](https://twitter.com/search?q=%23unityuiextensions) / [Facebook](https://www.facebook.com/UnityUIExtensions/) / [YouTube](https://www.youtube.com/@UnityUIExtensions)
> ## Chat live with the Unity UI Extensions community on Gitter here: > Ways to get in touch:
> >
> ## [UI Extensions Live Chat](https://gitter.im/Unity-UI-Extensions/Lobby) > - [Gitter Chat](https://gitter.im/Unity-UI-Extensions/Lobby) site for the UI Extensions project
> - [GitHub Discussions](https://github.com/Unity-UI-Extensions/com.unity.uiextensions/discussions), if you have any questions, queries or suggestions
----- -----
## [What is this repository for?](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/About) ## [What is this repository for?](https://unity-ui-extensions.github.io/About)
In this repository is a collection of extension scripts / effects and controls to enhance your Unity UI experience. These scripts have been gathered from many sources, combined and improved over time. In this repository is a collection of extension scripts / effects and controls to enhance your Unity UI experience. These scripts have been gathered from many sources, combined and improved over time.
@ -28,19 +29,19 @@ In this repository is a collection of extension scripts / effects and controls t
You can either download / fork this project to access the scripts, or you can also download these pre-compiled Unity Assets, chock full of goodness for each release: You can either download / fork this project to access the scripts, or you can also download these pre-compiled Unity Assets, chock full of goodness for each release:
## [Download - 2019.6 (aka 2.5)](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Downloads) ## [Download - 2019.6 (aka 2.5)](https://unity-ui-extensions.github.io/Downloads)
We have expanded where you can download the UnityPackage asset and widened the options to contribute to the project. We have expanded where you can download the UnityPackage asset and widened the options to contribute to the project.
> I will still stress however, ***contribution is optional***. **The assets / code will always remain FREE** > I will still stress however, ***contribution is optional***. **The assets / code will always remain FREE**
| [![Download from Itch.IO](https://bytebucket.org/UnityUIExtensions/unity-ui-extensions/wiki/SiteImages/itchio.png)](https://unityuiextensions.itch.io/uiextensions2-0 "Download from Itch.IO") | [![Download from Itch.IO](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/SiteImages/unionassets.png)](https://unionassets.com/unity-ui-extensions "Download from Union Assets") | [![Download from Itch.IO](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/SiteImages/patreon.jpg)](https://www.patreon.com/UnityUIExtensions "Support Unity UI Extensions on Patreon & download")| | [![Download from Itch.IO](https://unity-ui-extensions.github.io/SiteImages/itchio.png)](https://unityuiextensions.itch.io/uiextensions2-0 "Download from Itch.IO") | [![Download from Itch.IO](https://unity-ui-extensions.github.io/SiteImages/unionassets.png)](https://unionassets.com/unity-ui-extensions "Download from Union Assets") | [![Download from Itch.IO](https://unity-ui-extensions.github.io/SiteImages/patreon.jpg)](https://www.patreon.com/UnityUIExtensions "Support Unity UI Extensions on Patreon & download")|
| :--- | :--- | :--- | | :--- | :--- | :--- |
| [Grab from Itchio](https://unityuiextensions.itch.io/uiextensions2-0) | [Obtain via Union Assets](https://unionassets.com/unity-ui-extensions) |[Support through Patreon](https://www.patreon.com/UnityUIExtensions) | | [Grab from Itchio](https://unityuiextensions.itch.io/uiextensions2-0) | [Obtain via Union Assets](https://unionassets.com/unity-ui-extensions) |[Support through Patreon](https://www.patreon.com/UnityUIExtensions) |
> Still available to download on the [BitBucket site](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/downloads) if you prefer > Still available to download on the [GitHub site](https://github.com/Unity-UI-Extensions/com.unity.uiextensions/releases) if you prefer
To view previous releases, visit the [release archive](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Downloads) To view previous releases, visit the [release archive](https://unity-ui-extensions.github.io/Downloads)
----- -----
@ -50,15 +51,15 @@ If you wish to further support the Unity UI Extensions project itself, then you
All funds go to support the project, no matter the amount. **Donations in code are also extremely welcome** All funds go to support the project, no matter the amount. **Donations in code are also extremely welcome**
| | | |[![Donate via PayPal](https://www.paypalobjects.com/webstatic/mktg/Logo/pp-logo-150px.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=89L8T9N6BR7LJ "Donating via Paypal")|[![Buy us a Coffee](https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/5cbed8a4ae2b88347c06c923_BuyMeACoffee_blue-p-500.png)](https://ko-fi.com/uiextensions "Buy us a Coffee")|
|---|---| |-|-|
| [![Donate via PayPal](https://www.paypalobjects.com/webstatic/mktg/Logo/pp-logo-150px.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=89L8T9N6BR7LJ "Donating via Paypal") | [![Buy us a Coffee](https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/5cbed8a4ae2b88347c06c923_BuyMeACoffee_blue-p-500.png)](https://ko-fi.com/uiextensions "Buy us a Coffee") | |||
> (PayPal account not required and you can remain anonymous if you wish) > (PayPal account not required and you can remain anonymous if you wish)
----- -----
## [Getting Started](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/GettingStarted) ## [Getting Started](https://unity-ui-extensions.github.io/GettingStarted)
To get started with the project, here's a little guide: To get started with the project, here's a little guide:
@ -66,58 +67,92 @@ To get started with the project, here's a little guide:
----- -----
## [Updates:](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/ReleaseNotes/RELEASENOTES) ## [Updates:](https://unity-ui-extensions.github.io/ReleaseNotes/RELEASENOTES)
## Maintenance release 2019.6 - 2.5 - Bug squash ## Release 2.3 - Reanimation - 2022/02/07
Its been a while since the last update and although Unity keeps changing, thankfully the parts underneath do not. THanks to some awesome work by our contributors and the test teams, we made a run on some underlying bugs and issues. If you spot anything else, please log it on the BitBucket site for resolution. It has been a tough time for all since the last update, but things have been moving steadily along. In the past few months there has been a concerted effort to revamp and update the project ready for Unity 2022, as well as migrating the source repository over to GitHub and refreshing all the things.
We hope the new release is better for everyone and we have paid close attention to the editor menus and places to find all the controls for this release.
Be sure to also check out the "Examples" option in the Package Manager window to import the samples to your project. To get up to speed with the Unity UI Extensions, check out the [Getting Started](https://unity-ui-extensions.github.io/GettingStarted.html) Page.
> Be sure to logon to the new [Gitter Chat](https://gitter.im/Unity-UI-Extensions/Lobby) site for the UI Extensions project, if you have any questions, queries or suggestions > Ways to get in touch:
>
> - [Gitter Chat](https://gitter.im/Unity-UI-Extensions/Lobby) site for the UI Extensions project
> - [GitHub Discussions](https://github.com/Unity-UI-Extensions/com.unity.uiextensions/discussions), if you have any questions, queries or suggestions
> >
> Much easier that posting a question / issue on YouTube, Twitter or Facebook :D > Much easier that posting a question / issue on YouTube, Twitter or Facebook :D
>
> ## [UIExtensions Gitter Channel](https://gitter.im/Unity-UI-Extensions/Lobby)
### New / updated features ## Breaking changes
- Updated UI Line connector to use relative position instead of anchored position to verify if the Lines need updating. For customers upgrading from earlier versions of Unity to Unity 2020, please be aware of the Breaking change related to Text Based components. You will need to manually replace any UI using the older ```Text``` component and replace them with ```TextMeshPro``` versions. This is unavoidable due to Unity deprecating the Text component.
- Allow menu prefabs to not have to have canvas components. This allows you to use any type of prefab as a "menu". Adam Kapos mentions the concept on the Unite talk, https://youtu.be/wbmjturGbAQ?t=1654
- Updated segment line drawing for Line Lists. Seems Unity no longer needs UV's to be wrapped manually.
- Updated the AutoCompleteComboBox to display text as entered (instead of all lowercase)
- Updated the ComboBox to display text as entered (instead of all lowercase)
- Updated ComboBox Examples to include programmatic versions
- Further ComboBox improvements including:
* Upwards panel
* Start fixes
* Item Template resize
* Disabled sorting on combobox as it wasn't working
* Disabled Slider handle when not in use
* Updated Example
- Updated the new Input system switch and tested against 2021
### Examples / Examples / Examples > New users to 2022 are unaffected as all the Editor commands have been updated to use the newer TextMeshPro versions.
Examples can be found either in the UPM package manager window or via the extra downloadable UnityAsset from the Bitbucket site - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Downloads For more details, see the [deprecation notice](https://github.com/Unity-UI-Extensions/com.unity.uiextensions/discussions/428) on GitHub.
### Fixes ## Added
- Reordering issue resolved with ScrollRectOcclusion. - Added new FIFO based UI Line Render when dynamic line rendering is needed.
- Fixed Sorting at min and max positions for ScrollRect - Added ResetSelectableHighlight component.
- Updated ScrollToSelect script provided by zero3growlithe, tested and vastly reduces the previous jitter. Still present but barely noticeable now. - Added SetArc method to UICircle as requested.
- Fixed Issue # 363 Update Combobox control that takes multiple items programmatically, to only allow distinct items - Added new UIHorizontalScroller based on UIVerticalScroller.
- Fixed the issues where dragging outside the range slider handle causes the range to update. - Resolves #369 - Added OnHighlightChanged and OnPressChanged events for UI Button.
- Resolves an issue with Unity putting the previous controls vertex array in an uninitialised control. - Added error handling around setting Unity UI Components for Vertical/Horizontal ScrollSnaps.
- Applied J.R. Mitchell's fix for the Accordion Controls/Accordion/AccordionElement.cs - resolves: #364 - Added Editor Menu Option to create a Min/Max slider.
- Resolved issue where the Content Scroll snap issue with only 1 child. Resolves #362 - Added the ability to set a specific item for combobox controls on start and not just the first.
- Updated the PaginationManager to override if the ScrollSnap is in motion. - Added the ability to disable the combo boxes and make them read-only.
### Known issues ## Changed
No new issues in this release, but check the issues list for things we are currently working on: - Refresh FancyScrollView with the latest fixes
- All Text based components updated to use TextMeshPro from Unity 2022 **Breaking Change**
* [UI Extensions Issue log](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/issues?status=new&status=open) - Clean-up and reset pivots on scene start.
- Merged in feature/improved-ui-highlightable (pull request UILineRenderer - issues with specifying point locations at runtime #123).
- Merged in fix/ragesliderfix (pull request HorizontalScrollSnap Mask Area doesn't work when content created dynamically #125).
- Merged in fix/infinitescrollcontentsize (pull request Gradient initialization should be in Awake() #126).
- Merged in feature/controlTouchUp (pull request UILineRenderer mesh not updating in Editor scene view #127).
- Upgraded RangeSlider to work in both Horizontal and Vertical setups.
- Merged in RangeSlider-upgrade. (pull request Newtonsoft.Json.dll conflict #131)
- Updated UIVertical scroller to be 2022 compliant.
- Updated Curly UI to wait until end of the frame to recalculate positions.
- Updated Depth Texture sampler in UI Particles Shaders.
- Updated Points to always be an array of 1 when set to nothing for the Line Renderer.
- Updated Cooldown button to work with Keyboard input.
- Removed unneeded size calculation which caused some issues with mixed content.
- Resolved an issue whereby the last row in a flow layout group would not size correctly.
- Updated all components using "LayoutGroup" to override their OnDisable.
- Updated validation in the new MinMaxSlider.
- Updated Editor create options to add the correct Event System Input manager.
- Updated initialisation logic to not cause an endless loop in the TabNavigationHelper.
- Updated "Action" use to "UnityAction" to avoid Unity issues for DropDowns.
- Updated UIVerticalScroller for standards.
- Updated ReorderableList/ReorderableListElement to prevent creating a fake object for non-transferable items.
- Updated panel drawing for ComboBox controls and added DropdownOffset.
- Updated build issue with ReorderableListElement.
- Updated NonDrawingGraphic to require a CanvasRender, else it causes an error on run.
## Deprecated
- Marked ScrollPositionController as Obsolete, users should use the new Scoller.
- BestFitOutline - Deprecated in Unity 2020 onwards. (still available for earlier versions)
- NicerOutline - Deprecated in Unity 2020 onwards. (still available for earlier versions)
- Marked TileSizeFitter as obsolete as Unity has made this unworkable.
## Fixed
- Resolved issues with DisplayAbove and using a 0 ItemsToDisplay for ComboBox controls.
- Resolved startup issue that prevented the control from being used (Unity changed the start order in some instances), this was causing null reference issues with comboboxes.
- Patch fix for UILineRenderer.
- Resolves issue where the lower range value would become stuck when moving.
- Updated Infinite scroll to work with content of different sizes.
- Updated Dropdown list to NOT resize text Rect on draw.
- Revised the Curly UI fix as it was preventing the graphic from being updated in the scene view.
- Cleanup and ensuring the UIParticleSystem is disposed in onDestroy correctly.
- Clean up range slider unused variables.
* [UI Extensions Issue log](https://github.com/Unity-UI-Extensions/com.unity.uiextensions/issues)
## Upgrade Notes ## Upgrade Notes
@ -129,79 +164,88 @@ We recommend using the UPM delivery method. If you are using the Unity asset, th
For the full release history, follow the below link to the full release notes page. For the full release history, follow the below link to the full release notes page.
### [Release Notes](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/ReleaseNotes/RELEASENOTES) ### [Release Notes](https://unity-ui-extensions.github.io/ReleaseNotes/RELEASENOTES)
----- -----
## [Controls and extensions listed in this project](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Controls) ## [Controls and extensions listed in this project](https://unity-ui-extensions.github.io/Controls)
There are almost 70+ extension controls / effect and other utilities in the project which are listed on the following page: There are almost 70+ extension controls / effect and other utilities in the project which are listed on the following page:
> ## [Check out the control demos on our Tumblr page](https://www.tumblr.com/blog/unityuiextensions) > ## [Check out the control demos on our Tumblr page](https://www.tumblr.com/blog/unityuiextensions)
> >
> | [![UI Line Renderer](https://bytebucket.org/UnityUIExtensions/unity-ui-extensions/wiki/SiteImages/LineRenderer.gif)](https://www.tumblr.com/blog/unityuiextensions "UI Line Renderer") | [![UI Knob](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/SiteImages/UIKnob.gif)](https://www.tumblr.com/blog/unityuiextensions "UI Knob") | [![ScrollSnap](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/SiteImages/ScrollSnap.gif)](https://www.tumblr.com/blog/unityuiextensions "Scroll Snap")| > | [![UI Line Renderer](https://unity-ui-extensions.github.io/SiteImages/LineRenderer.gif)](https://www.tumblr.com/blog/unityuiextensions "UI Line Renderer") | [![UI Knob](https://unity-ui-extensions.github.io/SiteImages/UIKnob.gif)](https://www.tumblr.com/blog/unityuiextensions "UI Knob") | [![ScrollSnap](https://unity-ui-extensions.github.io/SiteImages/ScrollSnap.gif)](https://www.tumblr.com/blog/unityuiextensions "Scroll Snap")|
> | :--- | :--- | :--- | > | :--- | :--- | :--- |
> | [UI Line Renderer](https://www.tumblr.com/blog/unityuiextensions) | [UI Knob](https://www.tumblr.com/blog/unityuiextensions) |[Scroll Snap](https://www.tumblr.com/blog/unityuiextensions) | > | [UI Line Renderer](https://www.tumblr.com/blog/unityuiextensions) | [UI Knob](https://www.tumblr.com/blog/unityuiextensions) |[Scroll Snap](https://www.tumblr.com/blog/unityuiextensions) |
## [UI Extensions controls list](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Controls) ## [UI Extensions controls list](https://unity-ui-extensions.github.io/Controls)
[Controls](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Controls#markdown-header-controls)||||| [Controls](https://unity-ui-extensions.github.io/Controls.html#controls)
------|------|------|------|------|
Accordion|ColorPicker|Selection Box|UI Flippable|ComboBox
AutoComplete ComboBox|DropDown List|BoundToolTip|UIWindowBase|UI Knob
TextPic|Input Focus|Box Slider|Cooldown Button|Segmented Control
Stepper|Range Slider|Radial Slider|MultiTouch Scroll Rect|
||||
[Primitives](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Controls#markdown-header-primitives)||||| |Accordion|ColorPicker|Selection Box|UI Flippable|ComboBox|
------|------|------|------|------| |-|-|-|-|-|
UILineRenderer|UILineTextureRenderer|UICircle|DiamondGraph|UICornerCut |AutoComplete ComboBox|DropDown List|BoundToolTip|UIWindowBase|UI Knob|
UIPolygon|||| |TextPic|Input Focus|Box Slider|Cooldown Button|Segmented Control|
|||| |Stepper|Range Slider|Radial Slider|MultiTouch Scroll Rect|MinMax SLider|
[Layouts](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Controls#markdown-header-layouts)||||| [Primitives](https://unity-ui-extensions.github.io/Controls.html#primitives)
------|------|------|------|------|
Horizontal Scroll Snap|Vertical Scroll Snap|Flow Layout Group|Radial Layout|Tile Size Fitter
Scroll Snap (alt implementation)|Reorderable List|UI Vertical Scroller|Curved Layout|Table Layout
FancyScrollView|Card UI|Scroll Position Controller||
||||
[Effects](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Controls#markdown-header-effect_components)||||| |UILineRenderer|UILineTextureRenderer|UICircle|DiamondGraph|UICornerCut|
------|------|------|------|------| |-|-|-|-|-|
Best Fit Outline|Curved Text|Gradient|Gradient2|Letter Spacing |UIPolygon|UISquircle||||
NicerOutline|RaycastMask|UIFlippable|UIImageCrop|SoftAlphaMask
CylinderText|UIParticleSystem|CurlyUI|Shine Effect|Shader Effects
||||
[Additional Components](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Controls#markdown-header-additional_components)||||| [Layouts](https://unity-ui-extensions.github.io/Controls.html#layouts)
------|------|------|------|------|
ReturnKeyTrigger|TabNavigation|uGUITools|ScrollRectTweener|ScrollRectLinker |Horizontal Scroll Snap|Vertical Scroll Snap|Flow Layout Group|Radial Layout|Tile Size Fitter|
ScrollRectEx|UI_InfiniteScroll|UI_ScrollRectOcclusion|UIScrollToSelection|UISelectableExtension |-|-|-|-|-|
switchToRectTransform|ScrollConflictManager|CLFZ2 (Encryption)|DragCorrector|PPIViewer |Scroll Snap (alt implementation)|Reorderable List|UI Vertical Scroller|Curved Layout|Table Layout|
UI_TweenScale|UI_MagneticInfiniteScroll|UI_ScrollRectOcclusion|NonDrawingGraphic| |FancyScrollView|Card UI|Scroll Position Controller (obsolete)|Content Scroll Snap Horizontal|Scroller|
UILineConnector|UIHighlightable|Menu Manager|Pagination Manager| |ResizePanel|RescalePanel|RescaleDragPanel|||
||||
[Effects](https://unity-ui-extensions.github.io/Controls.html#effect-components)
|Best Fit Outline|Curved Text|Gradient|Gradient2|Letter Spacing|
|-|-|-|-|-|
|NicerOutline|RaycastMask|UIFlippable|UIImageCrop|SoftAlphaMask|
|CylinderText|UIParticleSystem|CurlyUI|Shine Effect|Shader Effects|
[Additional Components](https://unity-ui-extensions.github.io/Controls.html#additional-components)
|ReturnKeyTrigger|TabNavigation|uGUITools|ScrollRectTweener|ScrollRectLinker|
|-|-|-|-|-|
|ScrollRectEx|UI_InfiniteScroll|UI_ScrollRectOcclusion|UIScrollToSelection|UISelectableExtension|
|switchToRectTransform|ScrollConflictManager|CLFZ2 (Encryption)|DragCorrector|PPIViewer|
|UI_TweenScale|UI_MagneticInfiniteScroll|UI_ScrollRectOcclusion|NonDrawingGraphic|
|UILineConnector|
|UIHighlightable|Menu Manager|Pagination Manager|||
*More to come* *More to come*
----- -----
## [How do I get set up?](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/GettingStarted) ## [How do I get set up?](https://unity-ui-extensions.github.io/UPMInstallation.html)
As of Unity 2019, there are now two paths for getting access to the Unity UI Extensions project: The recommended way to add the Unity UI Extensions project to your solution is to use the Unity package Manager. Simply use the Unity Package Manager to reference the project to install it
- Unity 2019 or higher New for 2020, we have added OpenUPM support and the package can be installed using the following [OpenUPM CLI](https://openupm.com/docs/) command:
The recommended way to add the Unity UI Extensions project to your solution is to use the Unity package Manager. Simply use the [Unity Package Manager](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/UPMInstallation) to reference the project and install it.
Alternatively, you can also use the pre-compiled Unity packages if you wish, however, UPM offers full versioning support to allow you to switch versions as you wish. ```cli
`openupm add com.unity.uiextensions`
```
> For more details on using [OpenUPM CLI, check the docs here](https://github.com/openupm/openupm-cli#installation).
- Unity Package Manager - manual
Alternatively, you can also add the package manually through the Unity package manager using the scope ```com.unity.uiextensions```, see the [Unity Package Manager docs](https://learn.unity.com/tutorial/the-package-manager) for more information.
- Unity 2018 or lower - Unity 2018 or lower
The pre-compiled Unity assets are the only solution for Unity 2018 or earlier due to the changes in the Unity UI framework in Unity made for 2019. The pre-compiled Unity assets are the only solution for Unity 2018 or earlier due to the changes in the Unity UI framework in Unity made for 2019.
Either clone / download this repository to your machine and then copy the scripts in, or use the pre-packaged .UnityPackage for your version of Unity and import it as a custom package in to your project. Either clone / download this repository to your machine and then copy the scripts in, or use the pre-packaged .UnityPackage for your version of Unity and import it as a custom package in to your project.
## [Contribution guidelines](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/ContributionGuidelines) ## [Contribution guidelines](https://unity-ui-extensions.github.io/ContributionGuidelines)
Got a script you want added? Then just fork the bitbucket repository and submit a PR. All contributions accepted (including fixes) Got a script you want added? Then just fork the [GitHub repository](https://github.com/unity-UI-Extensions/com.unity.uiextensions) and submit a PR. All contributions accepted (including fixes)
Just ensure: Just ensure:
@ -209,21 +253,21 @@ Just ensure:
* The script uses the **Unity.UI.Extensions** namespace so they do not affect any other developments. * The script uses the **Unity.UI.Extensions** namespace so they do not affect any other developments.
* (optional) Add Component and Editor options where possible. (editor options are in the Editor\UIExtensionsMenuOptions.cs file) * (optional) Add Component and Editor options where possible. (editor options are in the Editor\UIExtensionsMenuOptions.cs file)
## [License](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/License) ## [License](https://raw.githubusercontent.com/Unity-UI-Extensions/com.unity.uiextensions/release/LICENSE.md)
All scripts conform to the BSD3 license and are free to use / distribute. See the [LICENSE](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/License) file for more information = All scripts conform to the BSD3 license and are free to use / distribute. See the [LICENSE](https://raw.githubusercontent.com/Unity-UI-Extensions/com.unity.uiextensions/release/LICENSE.md) file for more information =
## [Like what you see?](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/FurtherInfo) ## [Like what you see?](https://unity-ui-extensions.github.io/FurtherInfo)
All these scripts were put together for my latest book Unity3D UI Essentials All these scripts were put together for my latest book Unity3D UI Essentials
Check out the [page on my blog](http://bit.ly/Unity3DUIEssentials) for more details and learn all about the inner workings of the new Unity UI System. Check out the [page on my blog](http://bit.ly/Unity3DUIEssentials) for more details and learn all about the inner workings of the new Unity UI System.
## [The downloads](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Downloads) ## [The downloads](https://unity-ui-extensions.github.io/Downloads)
As this repo was created to support my new Unity UI Title ["Unity 3D UI Essentials"](http://bit.ly/Unity3DUIEssentials), in the downloads section you will find two custom assets (SpaceShip-DemoScene-Start.unitypackage and RollABallSample-Start.unitypackage). These are just here as starter scenes for doing UI tasks in the book. As this repo was created to support my new Unity UI Title ["Unity 3D UI Essentials"](http://bit.ly/Unity3DUIEssentials), in the downloads section you will find two custom assets (SpaceShip-DemoScene-Start.unitypackage and RollABallSample-Start.unitypackage). These are just here as starter scenes for doing UI tasks in the book.
I will add more sample scenes for the UI examples in this repository and detail them above over time. I will add more sample scenes for the UI examples in this repository and detail them above over time.
## [Previous Releases](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Downloads) ## [Previous Releases](https://unity-ui-extensions.github.io/Downloads)
Please see the [full downloads list](https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/wiki/Downloads) for all previous releases and their corresponding download links. Please see the [full downloads list](https://unity-ui-extensions.github.io/Downloads) for all previous releases and their corresponding download links.

View File

@ -82,7 +82,7 @@ Category {
return v; return v;
} }
sampler2D_float _CameraDepthTexture; UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
float _InvFade; float _InvFade;
fixed4 frag (v2f IN) : SV_Target fixed4 frag (v2f IN) : SV_Target

View File

@ -82,7 +82,7 @@ Category {
return v; return v;
} }
sampler2D_float _CameraDepthTexture; UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
float _InvFade; float _InvFade;
fixed4 frag (v2f i) : SV_Target fixed4 frag (v2f i) : SV_Target

View File

@ -2,8 +2,7 @@ Shader "UI Extensions/Particles/Additive (Soft)" {
Properties { Properties {
_MainTex ("Particle Texture", 2D) = "white" {} _MainTex ("Particle Texture", 2D) = "white" {}
_InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0 _InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0
_StencilComp ("Stencil Comparison", Float) = 8
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0 _Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0 _StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255 _StencilWriteMask ("Stencil Write Mask", Float) = 255
@ -81,7 +80,7 @@ Category {
return v; return v;
} }
sampler2D_float _CameraDepthTexture; UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
float _InvFade; float _InvFade;
fixed4 frag (v2f i) : SV_Target fixed4 frag (v2f i) : SV_Target

View File

@ -3,8 +3,7 @@ Properties {
_TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5) _TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
_MainTex ("Particle Texture", 2D) = "white" {} _MainTex ("Particle Texture", 2D) = "white" {}
_InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0 _InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0
_StencilComp ("Stencil Comparison", Float) = 8
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0 _Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0 _StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255 _StencilWriteMask ("Stencil Write Mask", Float) = 255
@ -82,7 +81,7 @@ Category {
return v; return v;
} }
sampler2D_float _CameraDepthTexture; UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
float _InvFade; float _InvFade;
fixed4 frag (v2f i) : SV_Target fixed4 frag (v2f i) : SV_Target

View File

@ -2,8 +2,7 @@ Shader "UI Extensions/Particles/Blend" {
Properties { Properties {
_MainTex ("Particle Texture", 2D) = "white" {} _MainTex ("Particle Texture", 2D) = "white" {}
_InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0 _InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0
_StencilComp ("Stencil Comparison", Float) = 8
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0 _Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0 _StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255 _StencilWriteMask ("Stencil Write Mask", Float) = 255
@ -81,7 +80,7 @@ Category {
return v; return v;
} }
sampler2D_float _CameraDepthTexture; UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
float _InvFade; float _InvFade;
fixed4 frag (v2f i) : SV_Target fixed4 frag (v2f i) : SV_Target

View File

@ -2,8 +2,7 @@ Shader "UI Extensions/Particles/Multiply" {
Properties { Properties {
_MainTex ("Particle Texture", 2D) = "white" {} _MainTex ("Particle Texture", 2D) = "white" {}
_InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0 _InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0
_StencilComp ("Stencil Comparison", Float) = 8
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0 _Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0 _StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255 _StencilWriteMask ("Stencil Write Mask", Float) = 255
@ -80,7 +79,7 @@ Category {
return v; return v;
} }
sampler2D_float _CameraDepthTexture; UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
float _InvFade; float _InvFade;
fixed4 frag (v2f i) : SV_Target fixed4 frag (v2f i) : SV_Target

View File

@ -2,8 +2,7 @@ Shader "UI Extensions/Particles/Multiply (Double)" {
Properties { Properties {
_MainTex ("Particle Texture", 2D) = "white" {} _MainTex ("Particle Texture", 2D) = "white" {}
_InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0 _InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0
_StencilComp ("Stencil Comparison", Float) = 8
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0 _Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0 _StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255 _StencilWriteMask ("Stencil Write Mask", Float) = 255
@ -81,7 +80,7 @@ Category {
return v; return v;
} }
sampler2D_float _CameraDepthTexture; UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
float _InvFade; float _InvFade;
fixed4 frag (v2f i) : SV_Target fixed4 frag (v2f i) : SV_Target

View File

@ -3,8 +3,7 @@ Properties {
_MainTex ("Particle Texture", 2D) = "white" {} _MainTex ("Particle Texture", 2D) = "white" {}
_InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0 _InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0
_StencilComp ("Stencil Comparison", Float) = 8
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0 _Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0 _StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255 _StencilWriteMask ("Stencil Write Mask", Float) = 255
@ -82,7 +81,7 @@ Category {
return v; return v;
} }
sampler2D_float _CameraDepthTexture; UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
float _InvFade; float _InvFade;
fixed4 frag (v2f IN) : SV_Target fixed4 frag (v2f IN) : SV_Target

View File

@ -3,7 +3,7 @@ Properties {
_EmisColor ("Emissive Color", Color) = (.2,.2,.2,0) _EmisColor ("Emissive Color", Color) = (.2,.2,.2,0)
_MainTex ("Particle Texture", 2D) = "white" {} _MainTex ("Particle Texture", 2D) = "white" {}
_StencilComp ("Stencil Comparison", Float) = 8 _StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0 _Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0 _StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255 _StencilWriteMask ("Stencil Write Mask", Float) = 255

View File

@ -5,83 +5,89 @@
namespace UnityEngine.UI.Extensions.ColorPicker namespace UnityEngine.UI.Extensions.ColorPicker
{ {
#if UNITY_2022_1_OR_NEWER
[RequireComponent(typeof(TMPro.TMP_Text))]
#else
[RequireComponent(typeof(Text))] [RequireComponent(typeof(Text))]
public class ColorLabel : MonoBehaviour
{
public ColorPickerControl picker;
public ColorValues type;
public string prefix = "R: ";
public float minValue = 0;
public float maxValue = 255;
public int precision = 0;
private Text label;
private void Awake()
{
label = GetComponent<Text>();
}
private void OnEnable()
{
if (Application.isPlaying && picker != null)
{
picker.onValueChanged.AddListener(ColorChanged);
picker.onHSVChanged.AddListener(HSVChanged);
}
}
private void OnDestroy()
{
if (picker != null)
{
picker.onValueChanged.RemoveListener(ColorChanged);
picker.onHSVChanged.RemoveListener(HSVChanged);
}
}
#if UNITY_EDITOR
private void OnValidate()
{
label = GetComponent<Text>();
UpdateValue();
}
#endif #endif
public class ColorLabel : MonoBehaviour
private void ColorChanged(Color color)
{ {
UpdateValue(); public ColorPickerControl picker;
}
private void HSVChanged(float hue, float sateration, float value) public ColorValues type;
{
UpdateValue();
}
private void UpdateValue() public string prefix = "R: ";
{ public float minValue = 0;
if (picker == null) public float maxValue = 255;
public int precision = 0;
#if UNITY_2022_1_OR_NEWER
private TMPro.TMP_Text label;
#else
private Text label;
#endif
private void Awake()
{ {
label.text = prefix + "-"; #if UNITY_2022_1_OR_NEWER
label = GetComponent<TMPro.TMP_Text>();
#else
label = GetComponent<Text>();
#endif
if (!label)
{
Debug.LogError($"{gameObject.name} does not have a Text component assigned for the {nameof(ColorLabel)}");
}
} }
else
{
float value = minValue + (picker.GetValue(type) * (maxValue - minValue));
label.text = prefix + ConvertToDisplayString(value); private void OnEnable()
{
if (Application.isPlaying && picker != null)
{
picker.onValueChanged.AddListener(ColorChanged);
picker.onHSVChanged.AddListener(HSVChanged);
}
}
private void OnDestroy()
{
if (picker != null)
{
picker.onValueChanged.RemoveListener(ColorChanged);
picker.onHSVChanged.RemoveListener(HSVChanged);
}
}
private void ColorChanged(Color color)
{
UpdateValue();
}
private void HSVChanged(float hue, float sateration, float value)
{
UpdateValue();
}
private void UpdateValue()
{
if (picker == null)
{
label.text = prefix + "-";
}
else
{
float value = minValue + (picker.GetValue(type) * (maxValue - minValue));
label.text = prefix + ConvertToDisplayString(value);
}
}
private string ConvertToDisplayString(float value)
{
if (precision > 0)
return value.ToString("f " + precision);
else
return Mathf.FloorToInt(value).ToString();
} }
} }
private string ConvertToDisplayString(float value)
{
if (precision > 0)
return value.ToString("f " + precision);
else
return Mathf.FloorToInt(value).ToString();
}
}
} }

View File

@ -1,8 +1,11 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 06851a815227e5044b0e3c1bf9b3a282 guid: dea5b3bc15f78d04d8dcae27500f784e
MonoImporter: MonoImporter:
externalObjects: {}
serializedVersion: 2 serializedVersion: 2
defaultReferences: [] defaultReferences: []
executionOrder: 0 executionOrder: 0
icon: {instanceID: 0} icon: {instanceID: 0}
userData: userData:
assetBundleName:
assetBundleVariant:

View File

@ -59,7 +59,9 @@ namespace UnityEngine.UI.Extensions.ColorPicker
private void OnDestroy() private void OnDestroy()
{ {
if (image.texture != null) if (image.texture != null)
{
DestroyImmediate(image.texture); DestroyImmediate(image.texture);
}
} }
#if UNITY_EDITOR #if UNITY_EDITOR

View File

@ -1,8 +1,11 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: ff46fbecea7739f4690e4285c88f53c5 guid: 9d3cae3318559ae449731a7db00c9bdd
MonoImporter: MonoImporter:
serializedVersion: 2 externalObjects: {}
defaultReferences: [] serializedVersion: 2
executionOrder: 0 defaultReferences: []
icon: {instanceID: 0} executionOrder: 0
userData: icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,12 +1,11 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 3d95ce8fba3dbbf4eb14411412169b88 guid: 97950dcfb7ac51c4c95431d68ad7bea5
timeCreated: 1442747317 MonoImporter:
licenseType: Free externalObjects: {}
MonoImporter: serializedVersion: 2
serializedVersion: 2 defaultReferences: []
defaultReferences: [] executionOrder: 0
executionOrder: 0 icon: {instanceID: 0}
icon: {instanceID: 0} userData:
userData: assetBundleName:
assetBundleName: assetBundleVariant:
assetBundleVariant:

View File

@ -1,8 +1,11 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 4f3189246d7fc204faba7a1e9c08e0af guid: 0e93d154602ed7e4787f2a7b9d3101b0
MonoImporter: MonoImporter:
externalObjects: {}
serializedVersion: 2 serializedVersion: 2
defaultReferences: [] defaultReferences: []
executionOrder: 0 executionOrder: 0
icon: {instanceID: 0} icon: {instanceID: 0}
userData: userData:
assetBundleName:
assetBundleVariant:

View File

@ -7,95 +7,95 @@ namespace UnityEngine.UI.Extensions.ColorPicker
{ {
[RequireComponent(typeof(InputField))] [RequireComponent(typeof(InputField))]
public class HexColorField : MonoBehaviour public class HexColorField : MonoBehaviour
{
public ColorPickerControl ColorPicker;
public bool displayAlpha;
private InputField hexInputField;
private const string hexRegex = "^#?(?:[0-9a-fA-F]{3,4}){1,2}$";
private void Awake()
{ {
hexInputField = GetComponent<InputField>(); public ColorPickerControl ColorPicker;
// Add listeners to keep text (and color) up to date public bool displayAlpha;
hexInputField.onEndEdit.AddListener(UpdateColor);
ColorPicker.onValueChanged.AddListener(UpdateHex);
}
private void OnDestroy() private InputField hexInputField;
{
hexInputField.onValueChanged.RemoveListener(UpdateColor);
ColorPicker.onValueChanged.RemoveListener(UpdateHex);
}
private void UpdateHex(Color newColor) private const string hexRegex = "^#?(?:[0-9a-fA-F]{3,4}){1,2}$";
{
hexInputField.text = ColorToHex(newColor);
}
private void UpdateColor(string newHex) private void Awake()
{
Color32 color;
if (HexToColor(newHex, out color))
ColorPicker.CurrentColor = color;
else
Debug.Log("hex value is in the wrong format, valid formats are: #RGB, #RGBA, #RRGGBB and #RRGGBBAA (# is optional)");
}
private string ColorToHex(Color32 color)
{
if (displayAlpha)
return string.Format("#{0:X2}{1:X2}{2:X2}{3:X2}", color.r, color.g, color.b, color.a);
else
return string.Format("#{0:X2}{1:X2}{2:X2}", color.r, color.g, color.b);
}
public static bool HexToColor(string hex, out Color32 color)
{
// Check if this is a valid hex string (# is optional)
if (System.Text.RegularExpressions.Regex.IsMatch(hex, hexRegex))
{ {
int startIndex = hex.StartsWith("#") ? 1 : 0; hexInputField = GetComponent<InputField>();
if (hex.Length == startIndex + 8) //#RRGGBBAA // Add listeners to keep text (and color) up to date
{ hexInputField.onEndEdit.AddListener(UpdateColor);
color = new Color32(byte.Parse(hex.Substring(startIndex, 2), NumberStyles.AllowHexSpecifier), ColorPicker.onValueChanged.AddListener(UpdateHex);
byte.Parse(hex.Substring(startIndex + 2, 2), NumberStyles.AllowHexSpecifier),
byte.Parse(hex.Substring(startIndex + 4, 2), NumberStyles.AllowHexSpecifier),
byte.Parse(hex.Substring(startIndex + 6, 2), NumberStyles.AllowHexSpecifier));
}
else if (hex.Length == startIndex + 6) //#RRGGBB
{
color = new Color32(byte.Parse(hex.Substring(startIndex, 2), NumberStyles.AllowHexSpecifier),
byte.Parse(hex.Substring(startIndex + 2, 2), NumberStyles.AllowHexSpecifier),
byte.Parse(hex.Substring(startIndex + 4, 2), NumberStyles.AllowHexSpecifier),
255);
}
else if (hex.Length == startIndex + 4) //#RGBA
{
color = new Color32(byte.Parse("" + hex[startIndex] + hex[startIndex], NumberStyles.AllowHexSpecifier),
byte.Parse("" + hex[startIndex + 1] + hex[startIndex + 1], NumberStyles.AllowHexSpecifier),
byte.Parse("" + hex[startIndex + 2] + hex[startIndex + 2], NumberStyles.AllowHexSpecifier),
byte.Parse("" + hex[startIndex + 3] + hex[startIndex + 3], NumberStyles.AllowHexSpecifier));
}
else //#RGB
{
color = new Color32(byte.Parse("" + hex[startIndex] + hex[startIndex], NumberStyles.AllowHexSpecifier),
byte.Parse("" + hex[startIndex + 1] + hex[startIndex + 1], NumberStyles.AllowHexSpecifier),
byte.Parse("" + hex[startIndex + 2] + hex[startIndex + 2], NumberStyles.AllowHexSpecifier),
255);
}
return true;
} }
else
private void OnDestroy()
{ {
color = new Color32(); hexInputField.onValueChanged.RemoveListener(UpdateColor);
return false; ColorPicker.onValueChanged.RemoveListener(UpdateHex);
}
private void UpdateHex(Color newColor)
{
hexInputField.text = ColorToHex(newColor);
}
private void UpdateColor(string newHex)
{
Color32 color;
if (HexToColor(newHex, out color))
ColorPicker.CurrentColor = color;
else
Debug.Log("hex value is in the wrong format, valid formats are: #RGB, #RGBA, #RRGGBB and #RRGGBBAA (# is optional)");
}
private string ColorToHex(Color32 color)
{
if (displayAlpha)
return string.Format("#{0:X2}{1:X2}{2:X2}{3:X2}", color.r, color.g, color.b, color.a);
else
return string.Format("#{0:X2}{1:X2}{2:X2}", color.r, color.g, color.b);
}
public static bool HexToColor(string hex, out Color32 color)
{
// Check if this is a valid hex string (# is optional)
if (System.Text.RegularExpressions.Regex.IsMatch(hex, hexRegex))
{
int startIndex = hex.StartsWith("#") ? 1 : 0;
if (hex.Length == startIndex + 8) //#RRGGBBAA
{
color = new Color32(byte.Parse(hex.Substring(startIndex, 2), NumberStyles.AllowHexSpecifier),
byte.Parse(hex.Substring(startIndex + 2, 2), NumberStyles.AllowHexSpecifier),
byte.Parse(hex.Substring(startIndex + 4, 2), NumberStyles.AllowHexSpecifier),
byte.Parse(hex.Substring(startIndex + 6, 2), NumberStyles.AllowHexSpecifier));
}
else if (hex.Length == startIndex + 6) //#RRGGBB
{
color = new Color32(byte.Parse(hex.Substring(startIndex, 2), NumberStyles.AllowHexSpecifier),
byte.Parse(hex.Substring(startIndex + 2, 2), NumberStyles.AllowHexSpecifier),
byte.Parse(hex.Substring(startIndex + 4, 2), NumberStyles.AllowHexSpecifier),
255);
}
else if (hex.Length == startIndex + 4) //#RGBA
{
color = new Color32(byte.Parse("" + hex[startIndex] + hex[startIndex], NumberStyles.AllowHexSpecifier),
byte.Parse("" + hex[startIndex + 1] + hex[startIndex + 1], NumberStyles.AllowHexSpecifier),
byte.Parse("" + hex[startIndex + 2] + hex[startIndex + 2], NumberStyles.AllowHexSpecifier),
byte.Parse("" + hex[startIndex + 3] + hex[startIndex + 3], NumberStyles.AllowHexSpecifier));
}
else //#RGB
{
color = new Color32(byte.Parse("" + hex[startIndex] + hex[startIndex], NumberStyles.AllowHexSpecifier),
byte.Parse("" + hex[startIndex + 1] + hex[startIndex + 1], NumberStyles.AllowHexSpecifier),
byte.Parse("" + hex[startIndex + 2] + hex[startIndex + 2], NumberStyles.AllowHexSpecifier),
255);
}
return true;
}
else
{
color = new Color32();
return false;
}
} }
} }
}
} }

View File

@ -52,7 +52,9 @@ namespace UnityEngine.UI.Extensions.ColorPicker
private void OnDestroy() private void OnDestroy()
{ {
if (image.texture != null) if (image.texture != null)
{
DestroyImmediate(image.texture); DestroyImmediate(image.texture);
}
} }
#if UNITY_EDITOR #if UNITY_EDITOR

View File

@ -1,6 +1,7 @@
///Credit perchik ///Credit perchik
///Sourced from - http://forum.unity3d.com/threads/receive-onclick-event-and-pass-it-on-to-lower-ui-elements.293642/ ///Sourced from - http://forum.unity3d.com/threads/receive-onclick-event-and-pass-it-on-to-lower-ui-elements.293642/
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -13,10 +14,9 @@ namespace UnityEngine.UI.Extensions
} }
[RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(RectTransform))]
[AddComponentMenu("UI/Extensions/AutoComplete ComboBox")] [AddComponentMenu("UI/Extensions/ComboBox/AutoComplete ComboBox")]
public class AutoCompleteComboBox : MonoBehaviour public class AutoCompleteComboBox : MonoBehaviour
{ {
public Color disabledTextColor;
public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it
/// <summary> /// <summary>
@ -24,17 +24,15 @@ namespace UnityEngine.UI.Extensions
/// <see cref="RemoveItem(string)"/> and <see cref="SetAvailableOptions(List{string})"/> methods as these also execute /// <see cref="RemoveItem(string)"/> and <see cref="SetAvailableOptions(List{string})"/> methods as these also execute
/// the required methods to update to the current collection. /// the required methods to update to the current collection.
/// </summary> /// </summary>
[Header("AutoComplete Box Items")]
public List<string> AvailableOptions; public List<string> AvailableOptions;
//private bool isInitialized = false;
private bool _isPanelActive = false; private bool _isPanelActive = false;
private bool _hasDrawnOnce = false; private bool _hasDrawnOnce = false;
private InputField _mainInput; private InputField _mainInput;
private RectTransform _inputRT; private RectTransform _inputRT;
//private Button _arrow_Button;
private RectTransform _rectTransform; private RectTransform _rectTransform;
private RectTransform _overlayRT; private RectTransform _overlayRT;
@ -52,11 +50,16 @@ namespace UnityEngine.UI.Extensions
private List<string> _prunedPanelItems; //items that used to show in the drop-down private List<string> _prunedPanelItems; //items that used to show in the drop-down
private Dictionary<string, GameObject> panelObjects; private Dictionary<string, GameObject> panelObjects;
private GameObject itemTemplate; private GameObject itemTemplate;
private bool _initialized;
public string Text { get; private set; } public string Text { get; private set; }
[Header("Properties")]
[SerializeField]
private bool isActive = true;
[SerializeField] [SerializeField]
private float _scrollBarWidth = 20.0f; private float _scrollBarWidth = 20.0f;
public float ScrollBarWidth public float ScrollBarWidth
@ -69,9 +72,6 @@ namespace UnityEngine.UI.Extensions
} }
} }
// private int scrollOffset; //offset of the selected item
// private int _selectedIndex = 0;
[SerializeField] [SerializeField]
private int _itemsToDisplay; private int _itemsToDisplay;
public int ItemsToDisplay public int ItemsToDisplay
@ -84,71 +84,85 @@ namespace UnityEngine.UI.Extensions
} }
} }
public bool SelectFirstItemOnStart = false; [SerializeField]
[SerializeField]
[Tooltip("Change input text color based on matching items")] [Tooltip("Change input text color based on matching items")]
private bool _ChangeInputTextColorBasedOnMatchingItems = false; private bool _ChangeInputTextColorBasedOnMatchingItems = false;
public bool InputColorMatching{ public bool InputColorMatching
get { return _ChangeInputTextColorBasedOnMatchingItems; } {
set get { return _ChangeInputTextColorBasedOnMatchingItems; }
{ set
_ChangeInputTextColorBasedOnMatchingItems = value; {
if (_ChangeInputTextColorBasedOnMatchingItems) { _ChangeInputTextColorBasedOnMatchingItems = value;
SetInputTextColor (); if (_ChangeInputTextColorBasedOnMatchingItems)
} {
} SetInputTextColor();
} }
}
}
public float DropdownOffset = 10f; public float DropdownOffset = 10f;
//TODO design as foldout for Inspector
public Color ValidSelectionTextColor = Color.green; public Color ValidSelectionTextColor = Color.green;
public Color MatchingItemsRemainingTextColor = Color.black; public Color MatchingItemsRemainingTextColor = Color.black;
public Color NoItemsRemainingTextColor = Color.red; public Color NoItemsRemainingTextColor = Color.red;
public AutoCompleteSearchType autocompleteSearchType = AutoCompleteSearchType.Linq; public AutoCompleteSearchType autocompleteSearchType = AutoCompleteSearchType.Linq;
[SerializeField]
private float dropdownOffset;
[SerializeField] [SerializeField]
private bool _displayPanelAbove = false; private bool _displayPanelAbove = false;
public bool SelectFirstItemOnStart = false;
[SerializeField]
private int selectItemIndexOnStart = 0;
private bool shouldSelectItemOnStart => SelectFirstItemOnStart || selectItemIndexOnStart > 0;
private bool _selectionIsValid = false; private bool _selectionIsValid = false;
[System.Serializable] [System.Serializable]
public class SelectionChangedEvent : UnityEngine.Events.UnityEvent<string, bool> { public class SelectionChangedEvent : Events.UnityEvent<string, bool> { }
}
[System.Serializable] [System.Serializable]
public class SelectionTextChangedEvent : UnityEngine.Events.UnityEvent<string> { public class SelectionTextChangedEvent : Events.UnityEvent<string> { }
}
[System.Serializable] [System.Serializable]
public class SelectionValidityChangedEvent : UnityEngine.Events.UnityEvent<bool> { public class SelectionValidityChangedEvent : Events.UnityEvent<bool> { }
}
// fires when input text is changed; [System.Serializable]
public SelectionTextChangedEvent OnSelectionTextChanged; public class ControlDisabledEvent : Events.UnityEvent<bool> { }
// fires when an Item gets selected / deselected (including when items are added/removed once this is possible)
public SelectionValidityChangedEvent OnSelectionValidityChanged; // fires when input text is changed;
// fires in both cases [Header("Events")]
public SelectionChangedEvent OnSelectionChanged; public SelectionTextChangedEvent OnSelectionTextChanged;
// fires when an Item gets selected / deselected (including when items are added/removed once this is possible)
public SelectionValidityChangedEvent OnSelectionValidityChanged;
// fires in both cases
public SelectionChangedEvent OnSelectionChanged;
// fires when item is changed;
public ControlDisabledEvent OnControlDisabled;
public void Awake() public void Awake()
{ {
Initialize(); Initialize();
} }
public void Start() public void Start()
{ {
if (SelectFirstItemOnStart && AvailableOptions.Count > 0) { if (shouldSelectItemOnStart && AvailableOptions.Count > 0)
ToggleDropdownPanel (false); {
OnItemClicked (AvailableOptions [0]); SelectItemIndex(SelectFirstItemOnStart ? 0 : selectItemIndexOnStart);
} }
RedrawPanel(); RedrawPanel();
} }
private bool Initialize() private bool Initialize()
{ {
if (_initialized) return true;
bool success = true; bool success = true;
try try
{ {
@ -156,8 +170,6 @@ namespace UnityEngine.UI.Extensions
_inputRT = _rectTransform.Find("InputField").GetComponent<RectTransform>(); _inputRT = _rectTransform.Find("InputField").GetComponent<RectTransform>();
_mainInput = _inputRT.GetComponent<InputField>(); _mainInput = _inputRT.GetComponent<InputField>();
//_arrow_Button = _rectTransform.FindChild ("ArrowBtn").GetComponent<Button> ();
_overlayRT = _rectTransform.Find("Overlay").GetComponent<RectTransform>(); _overlayRT = _rectTransform.Find("Overlay").GetComponent<RectTransform>();
_overlayRT.gameObject.SetActive(false); _overlayRT.gameObject.SetActive(false);
@ -167,7 +179,6 @@ namespace UnityEngine.UI.Extensions
_slidingAreaRT = _scrollBarRT.Find("SlidingArea").GetComponent<RectTransform>(); _slidingAreaRT = _scrollBarRT.Find("SlidingArea").GetComponent<RectTransform>();
_scrollHandleRT = _slidingAreaRT.Find("Handle").GetComponent<RectTransform>(); _scrollHandleRT = _slidingAreaRT.Find("Handle").GetComponent<RectTransform>();
_itemsPanelRT = _scrollPanelRT.Find("Items").GetComponent<RectTransform>(); _itemsPanelRT = _scrollPanelRT.Find("Items").GetComponent<RectTransform>();
//itemPanelLayout = itemsPanelRT.gameObject.GetComponent<LayoutGroup>();
_canvas = GetComponentInParent<Canvas>(); _canvas = GetComponentInParent<Canvas>();
_canvasRT = _canvas.GetComponent<RectTransform>(); _canvasRT = _canvas.GetComponent<RectTransform>();
@ -191,6 +202,8 @@ namespace UnityEngine.UI.Extensions
_prunedPanelItems = new List<string>(); _prunedPanelItems = new List<string>();
_panelItems = new List<string>(); _panelItems = new List<string>();
_initialized = true;
RebuildPanel(); RebuildPanel();
return success; return success;
} }
@ -225,21 +238,25 @@ namespace UnityEngine.UI.Extensions
} }
} }
/// <summary>
/// Update the drop down selection to a specific index
/// </summary>
/// <param name="index"></param>
public void SelectItemIndex(int index)
{
ToggleDropdownPanel(false);
OnItemClicked(AvailableOptions[index]);
}
/// <summary> /// <summary>
/// Sets the given items as new content for the comboBox. Previous entries will be cleared. /// Sets the given items as new content for the comboBox. Previous entries will be cleared.
/// </summary> /// </summary>
/// <param name="newOptions">New entries.</param> /// <param name="newOptions">New entries.</param>
public void SetAvailableOptions(List<string> newOptions) public void SetAvailableOptions(List<string> newOptions)
{ {
var uniqueOptions = newOptions.Distinct().ToList(); var uniqueOptions = newOptions.Distinct().ToArray();
if (newOptions.Count != uniqueOptions.Count) SetAvailableOptions(uniqueOptions);
{
Debug.LogWarning($"{nameof(AutoCompleteComboBox)}.{nameof(SetAvailableOptions)}: items may only exists once. {newOptions.Count - uniqueOptions.Count} duplicates.");
}
this.AvailableOptions.Clear();
this.AvailableOptions = uniqueOptions;
this.RebuildPanel();
} }
/// <summary> /// <summary>
@ -255,18 +272,21 @@ namespace UnityEngine.UI.Extensions
} }
this.AvailableOptions.Clear(); this.AvailableOptions.Clear();
for (int i = 0; i < newOptions.Length; i++) for (int i = 0; i < newOptions.Length; i++)
{ {
this.AvailableOptions.Add(newOptions[i]); this.AvailableOptions.Add(newOptions[i]);
} }
this.RebuildPanel(); this.RebuildPanel();
this.RedrawPanel();
} }
public void ResetItems() public void ResetItems()
{ {
AvailableOptions.Clear(); AvailableOptions.Clear();
RebuildPanel(); RebuildPanel();
RedrawPanel();
} }
/// <summary> /// <summary>
@ -274,6 +294,11 @@ namespace UnityEngine.UI.Extensions
/// </summary> /// </summary>
private void RebuildPanel() private void RebuildPanel()
{ {
if (!_initialized)
{
Start();
}
if (_isPanelActive) ToggleDropdownPanel(); if (_isPanelActive) ToggleDropdownPanel();
//panel starts with all options //panel starts with all options
@ -310,8 +335,11 @@ namespace UnityEngine.UI.Extensions
if (i < AvailableOptions.Count) if (i < AvailableOptions.Count)
{ {
itemObjs[i].name = "Item " + i + " " + _panelItems[i]; itemObjs[i].name = "Item " + i + " " + _panelItems[i];
#if UNITY_2022_1_OR_NEWER
itemObjs[i].transform.Find("Text").GetComponent<TMPro.TMP_Text>().text = AvailableOptions[i]; //set the text value
#else
itemObjs[i].transform.Find("Text").GetComponent<Text>().text = AvailableOptions[i]; //set the text value itemObjs[i].transform.Find("Text").GetComponent<Text>().text = AvailableOptions[i]; //set the text value
#endif
Button itemBtn = itemObjs[i].GetComponent<Button>(); Button itemBtn = itemObjs[i].GetComponent<Button>();
itemBtn.onClick.RemoveAllListeners(); itemBtn.onClick.RemoveAllListeners();
string textOfItem = _panelItems[i]; //has to be copied for anonymous function or it gets garbage collected away string textOfItem = _panelItems[i]; //has to be copied for anonymous function or it gets garbage collected away
@ -322,7 +350,7 @@ namespace UnityEngine.UI.Extensions
panelObjects[_panelItems[i]] = itemObjs[i]; panelObjects[_panelItems[i]] = itemObjs[i];
} }
} }
SetInputTextColor (); SetInputTextColor();
} }
/// <summary> /// <summary>
@ -337,63 +365,39 @@ namespace UnityEngine.UI.Extensions
ToggleDropdownPanel(true); ToggleDropdownPanel(true);
} }
//private void UpdateSelected()
//{
// SelectedItem = (_selectedIndex > -1 && _selectedIndex < Items.Count) ? Items[_selectedIndex] : null;
// if (SelectedItem == null) return;
// bool hasImage = SelectedItem.Image != null;
// if (hasImage)
// {
// mainButton.img.sprite = SelectedItem.Image;
// mainButton.img.color = Color.white;
// //if (Interactable) mainButton.img.color = Color.white;
// //else mainButton.img.color = new Color(1, 1, 1, .5f);
// }
// else
// {
// mainButton.img.sprite = null;
// }
// mainButton.txt.text = SelectedItem.Caption;
// //update selected index color
// for (int i = 0; i < itemsPanelRT.childCount; i++)
// {
// panelItems[i].btnImg.color = (_selectedIndex == i) ? mainButton.btn.colors.highlightedColor : new Color(0, 0, 0, 0);
// }
//}
private void RedrawPanel() private void RedrawPanel()
{ {
float scrollbarWidth = _panelItems.Count > ItemsToDisplay ? _scrollBarWidth : 0f;//hide the scrollbar if there's not enough items float scrollbarWidth = _panelItems.Count > ItemsToDisplay ? _scrollBarWidth : 0f;//hide the scrollbar if there's not enough items
_scrollBarRT.gameObject.SetActive(_panelItems.Count > ItemsToDisplay); _scrollBarRT.gameObject.SetActive(_panelItems.Count > ItemsToDisplay);
float dropdownHeight = _itemsToDisplay > 0 ? _rectTransform.sizeDelta.y * Mathf.Min(_itemsToDisplay, _panelItems.Count) : _rectTransform.sizeDelta.y * _panelItems.Count;
dropdownHeight += dropdownOffset;
if (!_hasDrawnOnce || _rectTransform.sizeDelta != _inputRT.sizeDelta) if (!_hasDrawnOnce || _rectTransform.sizeDelta != _inputRT.sizeDelta)
{ {
_hasDrawnOnce = true; _hasDrawnOnce = true;
_inputRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x); _inputRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
_inputRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _rectTransform.sizeDelta.y); _inputRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _rectTransform.sizeDelta.y);
_scrollPanelRT.SetParent(transform, true);//break the scroll panel from the overlay var itemsRemaining = _panelItems.Count - ItemsToDisplay;
itemsRemaining = itemsRemaining < 0 ? 0 : itemsRemaining;
_scrollPanelRT.SetParent(transform, true);
_scrollPanelRT.anchoredPosition = _displayPanelAbove ? _scrollPanelRT.anchoredPosition = _displayPanelAbove ?
new Vector2(0, DropdownOffset + _rectTransform.sizeDelta.y * _panelItems.Count - 1) : new Vector2(0, dropdownOffset + dropdownHeight) :
new Vector2(0, -_rectTransform.sizeDelta.y); new Vector2(0, -(dropdownOffset + _rectTransform.sizeDelta.y));
//make the overlay fill the screen //make the overlay fill the screen
_overlayRT.SetParent(_canvas.transform, false); //attach it to top level object _overlayRT.SetParent(_canvas.transform, false);
_overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _canvasRT.sizeDelta.x); _overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _canvasRT.sizeDelta.x);
_overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _canvasRT.sizeDelta.y); _overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _canvasRT.sizeDelta.y);
_overlayRT.SetParent(transform, true);//reattach to this object _overlayRT.SetParent(transform, true);
_scrollPanelRT.SetParent(_overlayRT, true); //reattach the scrollpanel to the overlay _scrollPanelRT.SetParent(_overlayRT, true);
} }
if (_panelItems.Count < 1) return; if (_panelItems.Count < 1) return;
float dropdownHeight = _rectTransform.sizeDelta.y * Mathf.Min(_itemsToDisplay, _panelItems.Count) + DropdownOffset;
_scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight); _scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight);
_scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x); _scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
@ -402,7 +406,7 @@ namespace UnityEngine.UI.Extensions
_scrollBarRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, scrollbarWidth); _scrollBarRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, scrollbarWidth);
_scrollBarRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight); _scrollBarRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight);
if (scrollbarWidth == 0) _scrollHandleRT.gameObject.SetActive(false); else _scrollHandleRT.gameObject.SetActive(true); if (scrollbarWidth == 0) _scrollHandleRT.gameObject.SetActive(false); else _scrollHandleRT.gameObject.SetActive(true);
_slidingAreaRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 0); _slidingAreaRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 0);
_slidingAreaRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight - _scrollBarRT.sizeDelta.x); _slidingAreaRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight - _scrollBarRT.sizeDelta.x);
@ -413,7 +417,6 @@ namespace UnityEngine.UI.Extensions
Text = currText; Text = currText;
PruneItems(currText); PruneItems(currText);
RedrawPanel(); RedrawPanel();
//Debug.Log("value changed to: " + currText);
if (_panelItems.Count == 0) if (_panelItems.Count == 0)
{ {
@ -425,30 +428,36 @@ namespace UnityEngine.UI.Extensions
ToggleDropdownPanel(false); ToggleDropdownPanel(false);
} }
bool validity_changed = (_panelItems.Contains (Text) != _selectionIsValid); bool validity_changed = (_panelItems.Contains(Text) != _selectionIsValid);
_selectionIsValid = _panelItems.Contains (Text); _selectionIsValid = _panelItems.Contains(Text);
OnSelectionChanged.Invoke (Text, _selectionIsValid); OnSelectionChanged.Invoke(Text, _selectionIsValid);
OnSelectionTextChanged.Invoke (Text); OnSelectionTextChanged.Invoke(Text);
if(validity_changed){ if (validity_changed)
OnSelectionValidityChanged.Invoke (_selectionIsValid); {
} OnSelectionValidityChanged.Invoke(_selectionIsValid);
}
SetInputTextColor (); SetInputTextColor();
} }
private void SetInputTextColor(){ private void SetInputTextColor()
if (InputColorMatching) { {
if (_selectionIsValid) { if (InputColorMatching)
_mainInput.textComponent.color = ValidSelectionTextColor; {
} else if (_panelItems.Count > 0) { if (_selectionIsValid)
_mainInput.textComponent.color = MatchingItemsRemainingTextColor; {
} else { _mainInput.textComponent.color = ValidSelectionTextColor;
_mainInput.textComponent.color = NoItemsRemainingTextColor; }
} else if (_panelItems.Count > 0)
} {
} _mainInput.textComponent.color = MatchingItemsRemainingTextColor;
}
else
{
_mainInput.textComponent.color = NoItemsRemainingTextColor;
}
}
}
/// <summary> /// <summary>
/// Toggle the drop down list /// Toggle the drop down list
@ -456,6 +465,8 @@ namespace UnityEngine.UI.Extensions
/// <param name="directClick"> whether an item was directly clicked on</param> /// <param name="directClick"> whether an item was directly clicked on</param>
public void ToggleDropdownPanel(bool directClick = false) public void ToggleDropdownPanel(bool directClick = false)
{ {
if (!isActive) return;
_isPanelActive = !_isPanelActive; _isPanelActive = !_isPanelActive;
_overlayRT.gameObject.SetActive(_isPanelActive); _overlayRT.gameObject.SetActive(_isPanelActive);
@ -469,6 +480,20 @@ namespace UnityEngine.UI.Extensions
} }
} }
/// <summary>
/// Updates the control and sets its active status, determines whether the dropdown will open ot not
/// </summary>
/// <param name="status"></param>
public void SetActive(bool status)
{
if (status != isActive)
{
OnControlDisabled?.Invoke(status);
}
isActive = status;
}
private void PruneItems(string currText) private void PruneItems(string currText)
{ {
if (autocompleteSearchType == AutoCompleteSearchType.Linq) if (autocompleteSearchType == AutoCompleteSearchType.Linq)
@ -528,4 +553,4 @@ namespace UnityEngine.UI.Extensions
} }
} }
} }
} }

View File

@ -3,48 +3,61 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using static UnityEditor.Progress;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(RectTransform))]
[AddComponentMenu("UI/Extensions/ComboBox")] [AddComponentMenu("UI/Extensions/ComboBox/ComboBox")]
public class ComboBox : MonoBehaviour public class ComboBox : MonoBehaviour
{ {
public Color disabledTextColor; public DropDownListItem SelectedItem { get; private set; }
public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it
[Header("Combo Box Items")]
public List<string> AvailableOptions; public List<string> AvailableOptions;
[Header("Properties")]
[SerializeField]
private bool isActive = true;
[SerializeField] [SerializeField]
private float _scrollBarWidth = 20.0f; private float _scrollBarWidth = 20.0f;
[SerializeField] [SerializeField]
private int _itemsToDisplay; private int _itemsToDisplay;
//Sorting disabled as it causes issues. [SerializeField]
//[SerializeField] private float dropdownOffset;
//private bool _sortItems = true;
[SerializeField] [SerializeField]
private bool _displayPanelAbove = false; private bool _displayPanelAbove = false;
public bool SelectFirstItemOnStart = false;
[SerializeField]
private int selectItemIndexOnStart = 0;
private bool shouldSelectItemOnStart => SelectFirstItemOnStart || selectItemIndexOnStart > 0;
[System.Serializable] [System.Serializable]
public class SelectionChangedEvent : UnityEngine.Events.UnityEvent<string> public class SelectionChangedEvent : Events.UnityEvent<string> { }
{
} [Header("Events")]
// fires when item is changed; // fires when item is changed;
public SelectionChangedEvent OnSelectionChanged; public SelectionChangedEvent OnSelectionChanged;
[System.Serializable]
public class ControlDisabledEvent : Events.UnityEvent<bool> { }
// fires when item is changed;
public ControlDisabledEvent OnControlDisabled;
//private bool isInitialized = false; //private bool isInitialized = false;
private bool _isPanelActive = false; private bool _isPanelActive = false;
private bool _hasDrawnOnce = false; private bool _hasDrawnOnce = false;
private InputField _mainInput; private InputField _mainInput;
private RectTransform _inputRT; private RectTransform _inputRT;
private RectTransform _rectTransform; private RectTransform _rectTransform;
private RectTransform _overlayRT; private RectTransform _overlayRT;
private RectTransform _scrollPanelRT; private RectTransform _scrollPanelRT;
private RectTransform _scrollBarRT; private RectTransform _scrollBarRT;
@ -53,14 +66,11 @@ namespace UnityEngine.UI.Extensions
private RectTransform _itemsPanelRT; private RectTransform _itemsPanelRT;
private Canvas _canvas; private Canvas _canvas;
private RectTransform _canvasRT; private RectTransform _canvasRT;
private ScrollRect _scrollRect; private ScrollRect _scrollRect;
private List<string> _panelItems; //items that will get shown in the drop-down private List<string> _panelItems; //items that will get shown in the drop-down
private Dictionary<string, GameObject> panelObjects; private Dictionary<string, GameObject> panelObjects;
private GameObject itemTemplate; private GameObject itemTemplate;
private bool _initialized;
public string Text { get; private set; } public string Text { get; private set; }
@ -74,9 +84,6 @@ namespace UnityEngine.UI.Extensions
} }
} }
// private int scrollOffset; //offset of the selected item
// private int _selectedIndex = 0;
public int ItemsToDisplay public int ItemsToDisplay
{ {
get { return _itemsToDisplay; } get { return _itemsToDisplay; }
@ -94,11 +101,17 @@ namespace UnityEngine.UI.Extensions
public void Start() public void Start()
{ {
if (shouldSelectItemOnStart && AvailableOptions.Count > 0)
{
SelectItemIndex(SelectFirstItemOnStart ? 0 : selectItemIndexOnStart);
}
RedrawPanel(); RedrawPanel();
} }
private bool Initialize() private bool Initialize()
{ {
if (_initialized) return true;
bool success = true; bool success = true;
try try
{ {
@ -138,11 +151,22 @@ namespace UnityEngine.UI.Extensions
_panelItems = AvailableOptions.ToList(); _panelItems = AvailableOptions.ToList();
_initialized = true;
RebuildPanel(); RebuildPanel();
//RedrawPanel(); - causes an initialisation failure in U5
return success; return success;
} }
/// <summary>
/// Update the drop down selection to a specific index
/// </summary>
/// <param name="index"></param>
public void SelectItemIndex(int index)
{
ToggleDropdownPanel(false);
OnItemClicked(AvailableOptions[index]);
}
public void AddItem(string item) public void AddItem(string item)
{ {
AvailableOptions.Add(item); AvailableOptions.Add(item);
@ -157,26 +181,34 @@ namespace UnityEngine.UI.Extensions
public void SetAvailableOptions(List<string> newOptions) public void SetAvailableOptions(List<string> newOptions)
{ {
AvailableOptions.Clear(); var uniqueOptions = newOptions.Distinct().ToArray();
AvailableOptions = newOptions; SetAvailableOptions(uniqueOptions);
RebuildPanel();
} }
public void SetAvailableOptions(string[] newOptions) public void SetAvailableOptions(string[] newOptions)
{ {
AvailableOptions.Clear(); var uniqueOptions = newOptions.Distinct().ToList();
if (newOptions.Length != uniqueOptions.Count)
{
Debug.LogWarning($"{nameof(ComboBox)}.{nameof(SetAvailableOptions)}: items may only exists once. {newOptions.Length - uniqueOptions.Count} duplicates.");
}
this.AvailableOptions.Clear();
for (int i = 0; i < newOptions.Length; i++) for (int i = 0; i < newOptions.Length; i++)
{ {
AvailableOptions.Add(newOptions[i]); this.AvailableOptions.Add(newOptions[i]);
} }
RebuildPanel();
this.RebuildPanel();
this.RedrawPanel();
} }
public void ResetItems() public void ResetItems()
{ {
AvailableOptions.Clear(); AvailableOptions.Clear();
RebuildPanel(); RebuildPanel();
RedrawPanel();
} }
/// <summary> /// <summary>
@ -184,13 +216,17 @@ namespace UnityEngine.UI.Extensions
/// </summary> /// </summary>
private void RebuildPanel() private void RebuildPanel()
{ {
if (!_initialized)
{
Start();
}
//panel starts with all options //panel starts with all options
_panelItems.Clear(); _panelItems.Clear();
foreach (string option in AvailableOptions) foreach (string option in AvailableOptions)
{ {
_panelItems.Add(option.ToLower()); _panelItems.Add(option.ToLower());
} }
//if(_sortItems) _panelItems.Sort();
List<GameObject> itemObjs = new List<GameObject>(panelObjects.Values); List<GameObject> itemObjs = new List<GameObject>(panelObjects.Values);
panelObjects.Clear(); panelObjects.Clear();
@ -211,8 +247,11 @@ namespace UnityEngine.UI.Extensions
if (i < AvailableOptions.Count) if (i < AvailableOptions.Count)
{ {
itemObjs[i].name = "Item " + i + " " + _panelItems[i]; itemObjs[i].name = "Item " + i + " " + _panelItems[i];
#if UNITY_2022_1_OR_NEWER
itemObjs[i].transform.Find("Text").GetComponent<TMPro.TMP_Text>().text = AvailableOptions[i]; //set the text value
#else
itemObjs[i].transform.Find("Text").GetComponent<Text>().text = AvailableOptions[i]; //set the text value itemObjs[i].transform.Find("Text").GetComponent<Text>().text = AvailableOptions[i]; //set the text value
#endif
Button itemBtn = itemObjs[i].GetComponent<Button>(); Button itemBtn = itemObjs[i].GetComponent<Button>();
itemBtn.onClick.RemoveAllListeners(); itemBtn.onClick.RemoveAllListeners();
string textOfItem = _panelItems[i]; //has to be copied for anonymous function or it gets garbage collected away string textOfItem = _panelItems[i]; //has to be copied for anonymous function or it gets garbage collected away
@ -237,63 +276,39 @@ namespace UnityEngine.UI.Extensions
ToggleDropdownPanel(true); ToggleDropdownPanel(true);
} }
//private void UpdateSelected()
//{
// SelectedItem = (_selectedIndex > -1 && _selectedIndex < Items.Count) ? Items[_selectedIndex] : null;
// if (SelectedItem == null) return;
// bool hasImage = SelectedItem.Image != null;
// if (hasImage)
// {
// mainButton.img.sprite = SelectedItem.Image;
// mainButton.img.color = Color.white;
// //if (Interactable) mainButton.img.color = Color.white;
// //else mainButton.img.color = new Color(1, 1, 1, .5f);
// }
// else
// {
// mainButton.img.sprite = null;
// }
// mainButton.txt.text = SelectedItem.Caption;
// //update selected index color
// for (int i = 0; i < itemsPanelRT.childCount; i++)
// {
// panelItems[i].btnImg.color = (_selectedIndex == i) ? mainButton.btn.colors.highlightedColor : new Color(0, 0, 0, 0);
// }
//}
private void RedrawPanel() private void RedrawPanel()
{ {
float scrollbarWidth = _panelItems.Count > ItemsToDisplay ? _scrollBarWidth : 0f;//hide the scrollbar if there's not enough items float scrollbarWidth = _panelItems.Count > ItemsToDisplay ? _scrollBarWidth : 0f;//hide the scrollbar if there's not enough items
_scrollBarRT.gameObject.SetActive(_panelItems.Count > ItemsToDisplay); _scrollBarRT.gameObject.SetActive(_panelItems.Count > ItemsToDisplay);
float dropdownHeight = _itemsToDisplay > 0 ? _rectTransform.sizeDelta.y * Mathf.Min(_itemsToDisplay, _panelItems.Count) : _rectTransform.sizeDelta.y * _panelItems.Count;
dropdownHeight += dropdownOffset;
if (!_hasDrawnOnce || _rectTransform.sizeDelta != _inputRT.sizeDelta) if (!_hasDrawnOnce || _rectTransform.sizeDelta != _inputRT.sizeDelta)
{ {
_hasDrawnOnce = true; _hasDrawnOnce = true;
_inputRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x); _inputRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
_inputRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _rectTransform.sizeDelta.y); _inputRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _rectTransform.sizeDelta.y);
_scrollPanelRT.SetParent(transform, true);//break the scroll panel from the overlay var itemsRemaining = _panelItems.Count - ItemsToDisplay;
itemsRemaining = itemsRemaining < 0 ? 0 : itemsRemaining;
_scrollPanelRT.SetParent(transform, true);
_scrollPanelRT.anchoredPosition = _displayPanelAbove ? _scrollPanelRT.anchoredPosition = _displayPanelAbove ?
new Vector2(0, _rectTransform.sizeDelta.y * ItemsToDisplay - 1) : new Vector2(0, dropdownOffset + dropdownHeight) :
new Vector2(0, -_rectTransform.sizeDelta.y); new Vector2(0, -(dropdownOffset + _rectTransform.sizeDelta.y));
//make the overlay fill the screen //make the overlay fill the screen
_overlayRT.SetParent(_canvas.transform, false); //attach it to top level object _overlayRT.SetParent(_canvas.transform, false);
_overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _canvasRT.sizeDelta.x); _overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _canvasRT.sizeDelta.x);
_overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _canvasRT.sizeDelta.y); _overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _canvasRT.sizeDelta.y);
_overlayRT.SetParent(transform, true);//reattach to this object _overlayRT.SetParent(transform, true);
_scrollPanelRT.SetParent(_overlayRT, true); //reattach the scrollpanel to the overlay _scrollPanelRT.SetParent(_overlayRT, true);
} }
if (_panelItems.Count < 1) return; if (_panelItems.Count < 1) return;
float dropdownHeight = _rectTransform.sizeDelta.y * Mathf.Min(_itemsToDisplay, _panelItems.Count);
_scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight); _scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight);
_scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x); _scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
@ -312,7 +327,6 @@ namespace UnityEngine.UI.Extensions
{ {
Text = currText; Text = currText;
RedrawPanel(); RedrawPanel();
//Debug.Log("value changed to: " + currText);
if (_panelItems.Count == 0) if (_panelItems.Count == 0)
{ {
@ -332,6 +346,8 @@ namespace UnityEngine.UI.Extensions
/// <param name="directClick"> whether an item was directly clicked on</param> /// <param name="directClick"> whether an item was directly clicked on</param>
public void ToggleDropdownPanel(bool directClick) public void ToggleDropdownPanel(bool directClick)
{ {
if (!isActive) return;
_isPanelActive = !_isPanelActive; _isPanelActive = !_isPanelActive;
_overlayRT.gameObject.SetActive(_isPanelActive); _overlayRT.gameObject.SetActive(_isPanelActive);
@ -344,5 +360,18 @@ namespace UnityEngine.UI.Extensions
// scrollOffset = Mathf.RoundToInt(itemsPanelRT.anchoredPosition.y / _rectTransform.sizeDelta.y); // scrollOffset = Mathf.RoundToInt(itemsPanelRT.anchoredPosition.y / _rectTransform.sizeDelta.y);
} }
} }
/// <summary>
/// Updates the control and sets its active status, determines whether the dropdown will open ot not
/// </summary>
/// <param name="status"></param>
public void SetActive(bool status)
{
if (status != isActive)
{
OnControlDisabled?.Invoke(status);
}
isActive = status;
}
} }
} }

View File

@ -1,7 +1,6 @@
///Credit perchik ///Credit perchik
///Sourced from - http://forum.unity3d.com/threads/receive-onclick-event-and-pass-it-on-to-lower-ui-elements.293642/ ///Sourced from - http://forum.unity3d.com/threads/receive-onclick-event-and-pass-it-on-to-lower-ui-elements.293642/
using System.Collections.Generic; using System.Collections.Generic;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
@ -10,13 +9,20 @@ namespace UnityEngine.UI.Extensions
/// Extension to the UI class which creates a dropdown list /// Extension to the UI class which creates a dropdown list
/// </summary> /// </summary>
[RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(RectTransform))]
[AddComponentMenu("UI/Extensions/Dropdown List")] [AddComponentMenu("UI/Extensions/ComboBox/Dropdown List")]
public class DropDownList : MonoBehaviour public class DropDownList : MonoBehaviour
{ {
public Color disabledTextColor; public Color disabledTextColor;
public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it
public List<DropDownListItem> Items; [Header("Dropdown List Items")]
public List<DropDownListItem> Items;
[Header("Properties")]
[SerializeField]
private bool isActive = true;
public bool OverrideHighlighted = true; public bool OverrideHighlighted = true;
//private bool isInitialized = false; //private bool isInitialized = false;
@ -38,11 +44,12 @@ namespace UnityEngine.UI.Extensions
private ScrollRect _scrollRect; private ScrollRect _scrollRect;
private List<DropDownListButton> _panelItems; private List<DropDownListButton> _panelItems = new List<DropDownListButton>();
private GameObject _itemTemplate; private GameObject _itemTemplate;
private bool _initialized;
[SerializeField] [SerializeField]
private float _scrollBarWidth = 20.0f; private float _scrollBarWidth = 20.0f;
public float ScrollBarWidth public float ScrollBarWidth
{ {
@ -69,32 +76,45 @@ namespace UnityEngine.UI.Extensions
} }
} }
public bool SelectFirstItemOnStart = false; [SerializeField]
private float dropdownOffset;
[SerializeField] [SerializeField]
private bool _displayPanelAbove = false; private bool _displayPanelAbove = false;
[SerializeField] public bool SelectFirstItemOnStart = false;
[Tooltip("Override the Text width for the values.")]
private bool _overrideTextWidth = true;
[System.Serializable] [SerializeField]
public class SelectionChangedEvent : UnityEngine.Events.UnityEvent<int> { } private int selectItemIndexOnStart = 0;
// fires when item is changed; private bool shouldSelectItemOnStart => SelectFirstItemOnStart || selectItemIndexOnStart > 0;
public SelectionChangedEvent OnSelectionChanged;
public void Start() [System.Serializable]
public class SelectionChangedEvent : Events.UnityEvent<int> { }
// fires when item is changed;
[Header("Events")]
public SelectionChangedEvent OnSelectionChanged;
[System.Serializable]
public class ControlDisabledEvent : Events.UnityEvent<bool> { }
// fires when item is changed;
public ControlDisabledEvent OnControlDisabled;
public void Start()
{
Initialize();
if (shouldSelectItemOnStart && Items.Count > 0)
{
SelectItemIndex(SelectFirstItemOnStart ? 0 : selectItemIndexOnStart);
}
RedrawPanel();
}
private bool Initialize()
{ {
Initialize(); if (_initialized) return true;
if (SelectFirstItemOnStart && Items.Count > 0) {
ToggleDropdownPanel (false);
OnItemClicked (0);
}
RedrawPanel();
}
private bool Initialize()
{
bool success = true; bool success = true;
try try
{ {
@ -103,8 +123,6 @@ namespace UnityEngine.UI.Extensions
_overlayRT = _rectTransform.Find("Overlay").GetComponent<RectTransform>(); _overlayRT = _rectTransform.Find("Overlay").GetComponent<RectTransform>();
_overlayRT.gameObject.SetActive(false); _overlayRT.gameObject.SetActive(false);
_scrollPanelRT = _overlayRT.Find("ScrollPanel").GetComponent<RectTransform>(); _scrollPanelRT = _overlayRT.Find("ScrollPanel").GetComponent<RectTransform>();
_scrollBarRT = _scrollPanelRT.Find("Scrollbar").GetComponent<RectTransform>(); _scrollBarRT = _scrollPanelRT.Find("Scrollbar").GetComponent<RectTransform>();
_slidingAreaRT = _scrollBarRT.Find("SlidingArea").GetComponent<RectTransform>(); _slidingAreaRT = _scrollBarRT.Find("SlidingArea").GetComponent<RectTransform>();
@ -120,7 +138,6 @@ namespace UnityEngine.UI.Extensions
_scrollRect.movementType = ScrollRect.MovementType.Clamped; _scrollRect.movementType = ScrollRect.MovementType.Clamped;
_scrollRect.content = _itemsPanelRT; _scrollRect.content = _itemsPanelRT;
_itemTemplate = _rectTransform.Find("ItemTemplate").gameObject; _itemTemplate = _rectTransform.Find("ItemTemplate").gameObject;
_itemTemplate.SetActive(false); _itemTemplate.SetActive(false);
} }
@ -130,23 +147,32 @@ namespace UnityEngine.UI.Extensions
Debug.LogError("Something is setup incorrectly with the dropdownlist component causing a Null Reference Exception"); Debug.LogError("Something is setup incorrectly with the dropdownlist component causing a Null Reference Exception");
success = false; success = false;
} }
_initialized = true;
_panelItems = new List<DropDownListButton>(); RebuildPanel();
RebuildPanel();
RedrawPanel(); RedrawPanel();
return success; return success;
} }
// currently just using items in the list instead of being able to add to it. /// <summary>
/// <summary> /// Update the drop down selection to a specific index
/// Rebuilds the list from a new collection.
/// </summary> /// </summary>
/// <remarks> /// <param name="index"></param>
/// NOTE, this will clear all existing items public void SelectItemIndex(int index)
/// </remarks> {
/// <param name="list"></param> ToggleDropdownPanel(false);
public void RefreshItems(params object[] list) OnItemClicked(index);
}
// currently just using items in the list instead of being able to add to it.
/// <summary>
/// Rebuilds the list from a new collection.
/// </summary>
/// <remarks>
/// NOTE, this will clear all existing items
/// </remarks>
/// <param name="list"></param>
public void RefreshItems(params object[] list)
{ {
Items.Clear(); Items.Clear();
List<DropDownListItem> ddItems = new List<DropDownListItem>(); List<DropDownListItem> ddItems = new List<DropDownListItem>();
@ -171,72 +197,80 @@ namespace UnityEngine.UI.Extensions
} }
Items.AddRange(ddItems); Items.AddRange(ddItems);
RebuildPanel(); RebuildPanel();
} RedrawPanel();
}
/// <summary> /// <summary>
/// Adds an additional item to the drop down list (recommended) /// Adds an additional item to the drop down list (recommended)
/// </summary> /// </summary>
/// <param name="item">Item of type DropDownListItem</param> /// <param name="item">Item of type DropDownListItem</param>
public void AddItem(DropDownListItem item) public void AddItem(DropDownListItem item)
{ {
Items.Add(item); Items.Add(item);
RebuildPanel(); RebuildPanel();
} RedrawPanel();
}
/// <summary> /// <summary>
/// Adds an additional drop down list item using a string name /// Adds an additional drop down list item using a string name
/// </summary> /// </summary>
/// <param name="item">Item of type String</param> /// <param name="item">Item of type String</param>
public void AddItem(string item) public void AddItem(string item)
{ {
Items.Add(new DropDownListItem(caption: (string)item)); Items.Add(new DropDownListItem(caption: (string)item));
RebuildPanel(); RebuildPanel();
} RedrawPanel();
}
/// <summary> /// <summary>
/// Adds an additional drop down list item using a sprite image /// Adds an additional drop down list item using a sprite image
/// </summary> /// </summary>
/// <param name="item">Item of type UI Sprite</param> /// <param name="item">Item of type UI Sprite</param>
public void AddItem(Sprite item) public void AddItem(Sprite item)
{ {
Items.Add(new DropDownListItem(image: (Sprite)item)); Items.Add(new DropDownListItem(image: (Sprite)item));
RebuildPanel(); RebuildPanel();
} RedrawPanel();
}
/// <summary> /// <summary>
/// Removes an item from the drop down list (recommended) /// Removes an item from the drop down list (recommended)
/// </summary> /// </summary>
/// <param name="item">Item of type DropDownListItem</param> /// <param name="item">Item of type DropDownListItem</param>
public void RemoveItem(DropDownListItem item) public void RemoveItem(DropDownListItem item)
{ {
Items.Remove(item); Items.Remove(item);
RebuildPanel(); RebuildPanel();
} RedrawPanel();
}
/// <summary> /// <summary>
/// Removes an item from the drop down list item using a string name /// Removes an item from the drop down list item using a string name
/// </summary> /// </summary>
/// <param name="item">Item of type String</param> /// <param name="item">Item of type String</param>
public void RemoveItem(string item) public void RemoveItem(string item)
{ {
Items.Remove(new DropDownListItem(caption: (string)item)); Items.Remove(new DropDownListItem(caption: (string)item));
RebuildPanel(); RebuildPanel();
} RedrawPanel();
}
/// <summary> /// <summary>
/// Removes an item from the drop down list item using a sprite image /// Removes an item from the drop down list item using a sprite image
/// </summary> /// </summary>
/// <param name="item">Item of type UI Sprite</param> /// <param name="item">Item of type UI Sprite</param>
public void RemoveItem(Sprite item) public void RemoveItem(Sprite item)
{ {
Items.Remove(new DropDownListItem(image: (Sprite)item)); Items.Remove(new DropDownListItem(image: (Sprite)item));
RebuildPanel(); RebuildPanel();
} RedrawPanel();
}
public void ResetItems() public void ResetItems()
{ {
Items.Clear(); Items.Clear();
RebuildPanel(); RebuildPanel();
RedrawPanel();
} }
/// <summary> /// <summary>
@ -246,6 +280,11 @@ namespace UnityEngine.UI.Extensions
{ {
if (Items.Count == 0) return; if (Items.Count == 0) return;
if (!_initialized)
{
Start();
}
int indx = _panelItems.Count; int indx = _panelItems.Count;
while (_panelItems.Count < Items.Count) while (_panelItems.Count < Items.Count)
{ {
@ -302,9 +341,6 @@ namespace UnityEngine.UI.Extensions
{ {
_mainButton.img.sprite = SelectedItem.Image; _mainButton.img.sprite = SelectedItem.Image;
_mainButton.img.color = Color.white; _mainButton.img.color = Color.white;
//if (Interactable) mainButton.img.color = Color.white;
//else mainButton.img.color = new Color(1, 1, 1, .5f);
} }
else else
{ {
@ -316,7 +352,6 @@ namespace UnityEngine.UI.Extensions
//update selected index color //update selected index color
if (OverrideHighlighted) if (OverrideHighlighted)
{ {
for (int i = 0; i < _itemsPanelRT.childCount; i++) for (int i = 0; i < _itemsPanelRT.childCount; i++)
{ {
_panelItems[i].btnImg.color = (_selectedIndex == i) ? _mainButton.btn.colors.highlightedColor : new Color(0, 0, 0, 0); _panelItems[i].btnImg.color = (_selectedIndex == i) ? _mainButton.btn.colors.highlightedColor : new Color(0, 0, 0, 0);
@ -324,40 +359,40 @@ namespace UnityEngine.UI.Extensions
} }
} }
private void RedrawPanel()
private void RedrawPanel()
{ {
float scrollbarWidth = Items.Count > ItemsToDisplay ? _scrollBarWidth : 0f;//hide the scrollbar if there's not enough items float scrollbarWidth = _panelItems.Count > ItemsToDisplay ? _scrollBarWidth : 0f;//hide the scrollbar if there's not enough items
_scrollBarRT.gameObject.SetActive(_panelItems.Count > ItemsToDisplay);
if (!_hasDrawnOnce || _rectTransform.sizeDelta != _mainButton.rectTransform.sizeDelta) float dropdownHeight = _itemsToDisplay > 0 ? _rectTransform.sizeDelta.y * Mathf.Min(_itemsToDisplay, _panelItems.Count) : _rectTransform.sizeDelta.y * _panelItems.Count;
dropdownHeight += dropdownOffset;
if (!_hasDrawnOnce || _rectTransform.sizeDelta != _mainButton.rectTransform.sizeDelta)
{ {
_hasDrawnOnce = true; _hasDrawnOnce = true;
_mainButton.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x); _mainButton.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
_mainButton.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _rectTransform.sizeDelta.y); _mainButton.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _rectTransform.sizeDelta.y);
if (_overrideTextWidth)
{
_mainButton.txt.rectTransform.offsetMax = new Vector2(4, 0);
}
_scrollPanelRT.SetParent(transform, true);//break the scroll panel from the overlay var itemsRemaining = _panelItems.Count - ItemsToDisplay;
_scrollPanelRT.anchoredPosition = _displayPanelAbove ? itemsRemaining = itemsRemaining < 0 ? 0 : itemsRemaining;
new Vector2(0, _rectTransform.sizeDelta.y * ItemsToDisplay - 1) :
new Vector2(0, -_rectTransform.sizeDelta.y);
//make the overlay fill the screen _scrollPanelRT.SetParent(transform, true);
_overlayRT.SetParent(_canvas.transform, false); //attach it to top level object _scrollPanelRT.anchoredPosition = _displayPanelAbove ?
new Vector2(0, dropdownOffset + dropdownHeight) :
new Vector2(0, -(dropdownOffset + _rectTransform.sizeDelta.y));
//make the overlay fill the screen
_overlayRT.SetParent(_canvas.transform, false);
_overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _canvasRT.sizeDelta.x); _overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _canvasRT.sizeDelta.x);
_overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _canvasRT.sizeDelta.y); _overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _canvasRT.sizeDelta.y);
_overlayRT.SetParent(transform, true);//reattach to this object _overlayRT.SetParent(transform, true);
_scrollPanelRT.SetParent(_overlayRT, true); //reattach the scrollpanel to the overlay _scrollPanelRT.SetParent(_overlayRT, true);
} }
if (Items.Count < 1) return; if (_panelItems.Count < 1) return;
float dropdownHeight = _rectTransform.sizeDelta.y * Mathf.Min(_itemsToDisplay, Items.Count); _scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight);
_scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight);
_scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x); _scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
_itemsPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _scrollPanelRT.sizeDelta.x - scrollbarWidth - 5); _itemsPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _scrollPanelRT.sizeDelta.x - scrollbarWidth - 5);
@ -377,6 +412,8 @@ namespace UnityEngine.UI.Extensions
/// <param name="directClick"> whether an item was directly clicked on</param> /// <param name="directClick"> whether an item was directly clicked on</param>
public void ToggleDropdownPanel(bool directClick) public void ToggleDropdownPanel(bool directClick)
{ {
if (!isActive) return;
_overlayRT.transform.localScale = new Vector3(1, 1, 1); _overlayRT.transform.localScale = new Vector3(1, 1, 1);
_scrollBarRT.transform.localScale = new Vector3(1, 1, 1); _scrollBarRT.transform.localScale = new Vector3(1, 1, 1);
_isPanelActive = !_isPanelActive; _isPanelActive = !_isPanelActive;
@ -390,5 +427,18 @@ namespace UnityEngine.UI.Extensions
// scrollOffset = Mathf.RoundToInt(itemsPanelRT.anchoredPosition.y / _rectTransform.sizeDelta.y); // scrollOffset = Mathf.RoundToInt(itemsPanelRT.anchoredPosition.y / _rectTransform.sizeDelta.y);
} }
} }
}
} /// <summary>
/// Updates the control and sets its active status, determines whether the dropdown will open ot not
/// </summary>
/// <param name="status"></param>
public void SetActive(bool status)
{
if (status != isActive)
{
OnControlDisabled?.Invoke(status);
}
isActive = status;
}
}
}

View File

@ -8,7 +8,11 @@ namespace UnityEngine.UI.Extensions
{ {
public RectTransform rectTransform; public RectTransform rectTransform;
public Button btn; public Button btn;
#if UNITY_2022_1_OR_NEWER
public TMPro.TMP_Text txt;
#else
public Text txt; public Text txt;
#endif
public Image btnImg; public Image btnImg;
public Image img; public Image img;
public GameObject gameobject; public GameObject gameobject;
@ -19,7 +23,11 @@ namespace UnityEngine.UI.Extensions
rectTransform = btnObj.GetComponent<RectTransform>(); rectTransform = btnObj.GetComponent<RectTransform>();
btnImg = btnObj.GetComponent<Image>(); btnImg = btnObj.GetComponent<Image>();
btn = btnObj.GetComponent<Button>(); btn = btnObj.GetComponent<Button>();
#if UNITY_2022_1_OR_NEWER
txt = rectTransform.Find("Text").GetComponent<TMPro.TMP_Text>();
#else
txt = rectTransform.Find("Text").GetComponent<Text>(); txt = rectTransform.Find("Text").GetComponent<Text>();
#endif
img = rectTransform.Find("Image").GetComponent<Image>(); img = rectTransform.Find("Image").GetComponent<Image>();
} }
} }

View File

@ -2,6 +2,7 @@
///Sourced from - http://forum.unity3d.com/threads/receive-onclick-event-and-pass-it-on-to-lower-ui-elements.293642/ ///Sourced from - http://forum.unity3d.com/threads/receive-onclick-event-and-pass-it-on-to-lower-ui-elements.293642/
using System; using System;
using UnityEngine.Events;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
@ -76,9 +77,9 @@ namespace UnityEngine.UI.Extensions
set { _id = value; } set { _id = value; }
} }
public Action OnSelect = null; //action to be called when this item is selected public UnityAction OnSelect = null; //action to be called when this item is selected
internal Action OnUpdate = null; //action to be called when something changes. internal UnityAction OnUpdate = null; //action to be called when something changes.
/// <summary> /// <summary>
/// Constructor for Drop Down List panelItems /// Constructor for Drop Down List panelItems
@ -87,8 +88,8 @@ namespace UnityEngine.UI.Extensions
/// <param name="val">ID of the item </param> /// <param name="val">ID of the item </param>
/// <param name="image"></param> /// <param name="image"></param>
/// <param name="disabled">Should the item start disabled</param> /// <param name="disabled">Should the item start disabled</param>
/// <param name="onSelect">Action to be called when this item is selected</param> /// <param name="onSelect">UnityAction to be called when this item is selected</param>
public DropDownListItem(string caption = "", string inId = "", Sprite image = null, bool disabled = false, Action onSelect = null) public DropDownListItem(string caption = "", string inId = "", Sprite image = null, bool disabled = false, UnityAction onSelect = null)
{ {
_caption = caption; _caption = caption;
_image = image; _image = image;

View File

@ -7,15 +7,14 @@ using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[AddComponentMenu("UI/Extensions/Cooldown Button")] [AddComponentMenu("UI/Extensions/Cooldown Button")]
public class CooldownButton : MonoBehaviour, IPointerDownHandler public class CooldownButton : MonoBehaviour, IPointerDownHandler, ISubmitHandler
{ {
#region Sub-Classes #region Sub-Classes
[System.Serializable] [System.Serializable]
public class CooldownButtonEvent : UnityEvent<PointerEventData.InputButton> { } public class CooldownButtonEvent : UnityEvent<GameObject> { }
#endregion #endregion
#region Private variables #region Private variables
[SerializeField] [SerializeField]
private float cooldownTimeout; private float cooldownTimeout;
[SerializeField] [SerializeField]
@ -33,7 +32,7 @@ namespace UnityEngine.UI.Extensions
[SerializeField][ReadOnly] [SerializeField][ReadOnly]
private int cooldownPercentComplete; private int cooldownPercentComplete;
PointerEventData buttonSource; BaseEventData buttonSource;
#endregion #endregion
#region Public Properties #region Public Properties
@ -116,7 +115,6 @@ namespace UnityEngine.UI.Extensions
#endregion #endregion
#region Public Methods #region Public Methods
/// <summary> /// <summary>
/// Pause Cooldown without resetting values, allows Restarting of cooldown /// Pause Cooldown without resetting values, allows Restarting of cooldown
/// </summary> /// </summary>
@ -144,9 +142,9 @@ namespace UnityEngine.UI.Extensions
/// </summary> /// </summary>
public void StartCooldown() public void StartCooldown()
{ {
PointerEventData emptySource = new PointerEventData(EventSystem.current); BaseEventData emptySource = new BaseEventData(EventSystem.current);
buttonSource = emptySource; buttonSource = emptySource;
OnCooldownStart.Invoke(emptySource.button); OnCooldownStart.Invoke(emptySource.selectedObject);
cooldownTimeRemaining = cooldownTimeout; cooldownTimeRemaining = cooldownTimeout;
CooldownActive = cooldownInEffect = true; CooldownActive = cooldownInEffect = true;
} }
@ -161,7 +159,7 @@ namespace UnityEngine.UI.Extensions
cooldownPercentRemaining = 0; cooldownPercentRemaining = 0;
cooldownPercentComplete = 100; cooldownPercentComplete = 100;
cooldownActive = cooldownInEffect = false; cooldownActive = cooldownInEffect = false;
if (OnCoolDownFinish != null) OnCoolDownFinish.Invoke(buttonSource.button); OnCoolDownFinish?.Invoke(buttonSource.selectedObject);
} }
/// <summary> /// <summary>
@ -171,27 +169,38 @@ namespace UnityEngine.UI.Extensions
{ {
cooldownActive = cooldownInEffect = false; cooldownActive = cooldownInEffect = false;
} }
#endregion #endregion
#region IPointerDownHandler #region IPointerDownHandler
void IPointerDownHandler.OnPointerDown(PointerEventData eventData) void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
{
HandleButtonClick(eventData);
}
#endregion
#region ISubmitHandler
public void OnSubmit(BaseEventData eventData)
{
HandleButtonClick(eventData);
}
#endregion ISubmitHandler
#region Private Methods
public void HandleButtonClick(BaseEventData eventData)
{ {
buttonSource = eventData; buttonSource = eventData;
if (CooldownInEffect) if (CooldownInEffect)
{ {
if (OnButtonClickDuringCooldown != null) OnButtonClickDuringCooldown.Invoke(eventData.button); OnButtonClickDuringCooldown?.Invoke(buttonSource.selectedObject);
} }
if (!CooldownInEffect) if (!CooldownInEffect)
{ {
if(OnCooldownStart != null) OnCooldownStart.Invoke(eventData.button); OnCooldownStart?.Invoke(buttonSource.selectedObject);
cooldownTimeRemaining = cooldownTimeout; cooldownTimeRemaining = cooldownTimeout;
cooldownActive = cooldownInEffect = true; cooldownActive = cooldownInEffect = true;
} }
} }
#endregion Private Methods
#endregion
} }
} }

View File

@ -1,7 +1,6 @@
/// Credit Erdener Gonenc - @PixelEnvision /// Credit Erdener Gonenc - @PixelEnvision
/*USAGE: Simply use that instead of the regular ScrollRect */ /*USAGE: Simply use that instead of the regular ScrollRect */
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[AddComponentMenu ("UI/Extensions/MultiTouchScrollRect")] [AddComponentMenu ("UI/Extensions/MultiTouchScrollRect")]

View File

@ -6,7 +6,6 @@ using UnityEngine.Events;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[RequireComponent(typeof(RectTransform)), DisallowMultipleComponent] [RequireComponent(typeof(RectTransform)), DisallowMultipleComponent]
[AddComponentMenu("UI/Extensions/Re-orderable list")] [AddComponentMenu("UI/Extensions/Re-orderable list")]
public class ReorderableList : MonoBehaviour public class ReorderableList : MonoBehaviour
@ -31,11 +30,11 @@ namespace UnityEngine.UI.Extensions
// This sets every item size (when being dragged over this list) to the current size of the first element of this list // This sets every item size (when being dragged over this list) to the current size of the first element of this list
[Tooltip("Should items being dragged over this list have their sizes equalized?")] [Tooltip("Should items being dragged over this list have their sizes equalized?")]
public bool EqualizeSizesOnDrag = false; public bool EqualizeSizesOnDrag = false;
[Tooltip("Maximum number of items this container can hold")]
public int maxItems = int.MaxValue; public int maxItems = int.MaxValue;
[Header("UI Re-orderable Events")] [Header("UI Re-orderable Events")]
public ReorderableListHandler OnElementDropped = new ReorderableListHandler(); public ReorderableListHandler OnElementDropped = new ReorderableListHandler();
public ReorderableListHandler OnElementGrabbed = new ReorderableListHandler(); public ReorderableListHandler OnElementGrabbed = new ReorderableListHandler();
@ -62,7 +61,7 @@ namespace UnityEngine.UI.Extensions
} }
} }
Canvas GetCanvas() public Canvas GetCanvas()
{ {
Transform t = transform; Transform t = transform;
Canvas canvas = null; Canvas canvas = null;
@ -73,8 +72,7 @@ namespace UnityEngine.UI.Extensions
while (canvas == null && lvl < lvlLimit) while (canvas == null && lvl < lvlLimit)
{ {
canvas = t.gameObject.GetComponent<Canvas>(); if (!t.gameObject.TryGetComponent<Canvas>(out canvas))
if (canvas == null)
{ {
t = t.parent; t = t.parent;
} }
@ -95,7 +93,6 @@ namespace UnityEngine.UI.Extensions
private void Start() private void Start()
{ {
if (ContentLayout == null) if (ContentLayout == null)
{ {
Debug.LogError("You need to have a child LayoutGroup content set for the list: " + name, gameObject); Debug.LogError("You need to have a child LayoutGroup content set for the list: " + name, gameObject);
@ -114,7 +111,6 @@ namespace UnityEngine.UI.Extensions
Refresh(); Refresh();
} }
#region Nested type: ReorderableListEventStruct #region Nested type: ReorderableListEventStruct
[Serializable] [Serializable]
@ -136,13 +132,10 @@ namespace UnityEngine.UI.Extensions
#endregion #endregion
#region Nested type: ReorderableListHandler #region Nested type: ReorderableListHandler
[Serializable] [Serializable]
public class ReorderableListHandler : UnityEvent<ReorderableListEventStruct> public class ReorderableListHandler : UnityEvent<ReorderableListEventStruct> { }
{
}
public void TestReOrderableListTarget(ReorderableListEventStruct item) public void TestReOrderableListTarget(ReorderableListEventStruct item)
{ {
@ -152,4 +145,4 @@ namespace UnityEngine.UI.Extensions
#endregion #endregion
} }
} }

View File

@ -21,7 +21,6 @@ namespace UnityEngine.UI.Extensions
if(_rect)StartCoroutine(RefreshChildren()); if(_rect)StartCoroutine(RefreshChildren());
} }
public void OnTransformChildrenChanged() public void OnTransformChildrenChanged()
{ {
if(this.isActiveAndEnabled)StartCoroutine(RefreshChildren()); if(this.isActiveAndEnabled)StartCoroutine(RefreshChildren());

View File

@ -5,7 +5,11 @@ namespace UnityEngine.UI.Extensions
{ {
public class ReorderableListDebug : MonoBehaviour public class ReorderableListDebug : MonoBehaviour
{ {
#if UNITY_2022_1_OR_NEWER
public TMPro.TMP_Text DebugLabel;
#else
public Text DebugLabel; public Text DebugLabel;
#endif
void Awake() void Awake()
{ {

View File

@ -8,31 +8,29 @@ using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[RequireComponent(typeof(RectTransform), typeof(LayoutElement))] [RequireComponent(typeof(RectTransform), typeof(LayoutElement))]
public class ReorderableListElement : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler public class ReorderableListElement : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler
{ {
[Tooltip("Can this element be dragged?")] [Tooltip("Can this element be dragged?")]
[SerializeField] [SerializeField]
private bool IsGrabbable = true; private bool isGrabbable = true;
[Tooltip("Can this element be transfered to another list")] [Tooltip("Can this element be dropped in another container?")]
[SerializeField] [SerializeField]
private bool _isTransferable = true; private bool isTransferable = true;
[Tooltip("Can this element be dropped in space?")] [Tooltip("Can this element be dropped in space?")]
[SerializeField] [SerializeField]
private bool isDroppableInSpace = false; private bool isDroppableInSpace = false;
public bool IsTransferable public bool IsTransferable
{ {
get { return _isTransferable; } get { return isTransferable; }
set set
{ {
_canvasGroup = gameObject.GetOrAddComponent<CanvasGroup>(); _canvasGroup = gameObject.GetOrAddComponent<CanvasGroup>();
_canvasGroup.blocksRaycasts = value; _canvasGroup.blocksRaycasts = value;
_isTransferable = value; isTransferable = value;
} }
} }
@ -61,7 +59,6 @@ namespace UnityEngine.UI.Extensions
#region IBeginDragHandler Members #region IBeginDragHandler Members
public void OnBeginDrag(PointerEventData eventData) public void OnBeginDrag(PointerEventData eventData)
{ {
if (!_canvasGroup) { _canvasGroup = gameObject.GetOrAddComponent<CanvasGroup>(); } if (!_canvasGroup) { _canvasGroup = gameObject.GetOrAddComponent<CanvasGroup>(); }
@ -71,7 +68,7 @@ namespace UnityEngine.UI.Extensions
return; return;
//Can't drag, return... //Can't drag, return...
if (!_reorderableList.IsDraggable || !this.IsGrabbable) if (!_reorderableList.IsDraggable || !this.isGrabbable)
{ {
_draggingObject = null; _draggingObject = null;
return; return;
@ -142,12 +139,9 @@ namespace UnityEngine.UI.Extensions
_isDragging = true; _isDragging = true;
} }
#endregion #endregion
#region IDragHandler Members #region IDragHandler Members
public void OnDrag(PointerEventData eventData) public void OnDrag(PointerEventData eventData)
{ {
if (!_isDragging) if (!_isDragging)
@ -179,7 +173,6 @@ namespace UnityEngine.UI.Extensions
//If nothing found or the list is not dropable, put the fake element outside //If nothing found or the list is not dropable, put the fake element outside
if (_currentReorderableListRaycasted == null || _currentReorderableListRaycasted.IsDropable == false if (_currentReorderableListRaycasted == null || _currentReorderableListRaycasted.IsDropable == false
|| (_oldReorderableListRaycasted != _reorderableList && !IsTransferable)
|| ((_fakeElement.parent == _currentReorderableListRaycasted.Content || ((_fakeElement.parent == _currentReorderableListRaycasted.Content
? _currentReorderableListRaycasted.Content.childCount - 1 ? _currentReorderableListRaycasted.Content.childCount - 1
: _currentReorderableListRaycasted.Content.childCount) >= _currentReorderableListRaycasted.maxItems && !_currentReorderableListRaycasted.IsDisplacable) : _currentReorderableListRaycasted.Content.childCount) >= _currentReorderableListRaycasted.maxItems && !_currentReorderableListRaycasted.IsDisplacable)
@ -194,7 +187,7 @@ namespace UnityEngine.UI.Extensions
} }
} }
//Else find the best position on the list and put fake element on the right index //Else find the best position on the list and put fake element on the right index
else else if (_currentReorderableListRaycasted == _reorderableList || IsTransferable)
{ {
if (_currentReorderableListRaycasted.Content.childCount < _currentReorderableListRaycasted.maxItems && _fakeElement.parent != _currentReorderableListRaycasted.Content) if (_currentReorderableListRaycasted.Content.childCount < _currentReorderableListRaycasted.maxItems && _fakeElement.parent != _currentReorderableListRaycasted.Content)
{ {
@ -245,12 +238,9 @@ namespace UnityEngine.UI.Extensions
} }
} }
#endregion #endregion
#region Displacement #region Displacement
private void displaceElement(int targetIndex, Transform displaced) private void displaceElement(int targetIndex, Transform displaced)
{ {
_displacedFromIndex = targetIndex; _displacedFromIndex = targetIndex;
@ -343,7 +333,6 @@ namespace UnityEngine.UI.Extensions
} }
public void finishDisplacingElement() public void finishDisplacingElement()
{ {
if (_displacedObject.parent == null) if (_displacedObject.parent == null)
@ -355,12 +344,9 @@ namespace UnityEngine.UI.Extensions
_displacedObject = null; _displacedObject = null;
_displacedObjectLE = null; _displacedObjectLE = null;
} }
#endregion #endregion
#region IEndDragHandler Members #region IEndDragHandler Members
public void OnEndDrag(PointerEventData eventData) public void OnEndDrag(PointerEventData eventData)
{ {
_isDragging = false; _isDragging = false;
@ -473,11 +459,9 @@ namespace UnityEngine.UI.Extensions
} }
_canvasGroup.blocksRaycasts = true; _canvasGroup.blocksRaycasts = true;
} }
#endregion #endregion
private void CancelDrag()
void CancelDrag()
{ {
_isDragging = false; _isDragging = false;
//If it's a clone, delete it //If it's a clone, delete it
@ -556,4 +540,4 @@ namespace UnityEngine.UI.Extensions
_canvasGroup = gameObject.GetOrAddComponent<CanvasGroup>(); _canvasGroup = gameObject.GetOrAddComponent<CanvasGroup>();
} }
} }
} }

View File

@ -1,9 +1,7 @@
/// Credit David Gileadi /// Credit David Gileadi
/// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/pull-requests/12 /// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/pull-requests/12
using System;
using System.Collections; using System.Collections;
using UnityEngine.Events;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
@ -205,7 +203,11 @@ namespace UnityEngine.UI.Extensions
void ChangeTextColor(Color targetColor) void ChangeTextColor(Color targetColor)
{ {
#if UNITY_2022_1_OR_NEWER
var text = GetComponentInChildren<TMPro.TMP_Text>();
#else
var text = GetComponentInChildren<Text>(); var text = GetComponentInChildren<Text>();
#endif
if (!text) if (!text)
return; return;

View File

@ -40,13 +40,21 @@ namespace UnityEngine.UI.Extensions
//We want the test object to be either a UI element, a 2D element or a 3D element, so we'll get the appropriate components //We want the test object to be either a UI element, a 2D element or a 3D element, so we'll get the appropriate components
SpriteRenderer spriteRenderer; SpriteRenderer spriteRenderer;
Image image; Image image;
#if UNITY_2022_1_OR_NEWER
TMPro.TMP_Text text;
#else
Text text; Text text;
#endif
void Start() void Start()
{ {
spriteRenderer = transform.GetComponent<SpriteRenderer>(); spriteRenderer = transform.GetComponent<SpriteRenderer>();
image = transform.GetComponent<Image>(); image = transform.GetComponent<Image>();
#if UNITY_2022_1_OR_NEWER
text = transform.GetComponent<TMPro.TMP_Text>();
#else
text = transform.GetComponent<Text>(); text = transform.GetComponent<Text>();
#endif
} }
void Update() void Update()
@ -86,8 +94,6 @@ namespace UnityEngine.UI.Extensions
{ {
GetComponent<UnityEngine.Renderer>().material.color = color; GetComponent<UnityEngine.Renderer>().material.color = color;
} }
} }
} }
} }

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d7bdc7e70331fe24aba2c9549f84c657
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,371 +1,371 @@
///Credit judah4 ///Credit judah4
///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/ ///Sourced from - http://forum.unity3d.com/threads/color-picker.267043/
using System; using System;
using UnityEngine.Events; using UnityEngine.Events;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(RectTransform))]
[AddComponentMenu("UI/Extensions/BoxSlider")] [AddComponentMenu("UI/Extensions/Sliders/BoxSlider")]
public class BoxSlider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement public class BoxSlider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement
{ {
public enum Direction public enum Direction
{ {
LeftToRight, LeftToRight,
RightToLeft, RightToLeft,
BottomToTop, BottomToTop,
TopToBottom, TopToBottom,
} }
[Serializable] [Serializable]
public class BoxSliderEvent : UnityEvent<float, float> { } public class BoxSliderEvent : UnityEvent<float, float> { }
[SerializeField] [SerializeField]
private RectTransform m_HandleRect; private RectTransform m_HandleRect;
public RectTransform HandleRect { get { return m_HandleRect; } set { if (SetClass(ref m_HandleRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } } public RectTransform HandleRect { get { return m_HandleRect; } set { if (SetClass(ref m_HandleRect, value)) { UpdateCachedReferences(); UpdateVisuals(); } } }
[Space(6)] [Space(6)]
[SerializeField] [SerializeField]
private float m_MinValue = 0; private float m_MinValue = 0;
public float MinValue { get { return m_MinValue; } set { if (SetStruct(ref m_MinValue, value)) { SetX(m_ValueX); SetY(m_ValueY); UpdateVisuals(); } } } public float MinValue { get { return m_MinValue; } set { if (SetStruct(ref m_MinValue, value)) { SetX(m_ValueX); SetY(m_ValueY); UpdateVisuals(); } } }
[SerializeField] [SerializeField]
private float m_MaxValue = 1; private float m_MaxValue = 1;
public float MaxValue { get { return m_MaxValue; } set { if (SetStruct(ref m_MaxValue, value)) { SetX(m_ValueX); SetY(m_ValueY); UpdateVisuals(); } } } public float MaxValue { get { return m_MaxValue; } set { if (SetStruct(ref m_MaxValue, value)) { SetX(m_ValueX); SetY(m_ValueY); UpdateVisuals(); } } }
[SerializeField] [SerializeField]
private bool m_WholeNumbers = false; private bool m_WholeNumbers = false;
public bool WholeNumbers { get { return m_WholeNumbers; } set { if (SetStruct(ref m_WholeNumbers, value)) { SetX(m_ValueX); SetY(m_ValueY); UpdateVisuals(); } } } public bool WholeNumbers { get { return m_WholeNumbers; } set { if (SetStruct(ref m_WholeNumbers, value)) { SetX(m_ValueX); SetY(m_ValueY); UpdateVisuals(); } } }
[SerializeField] [SerializeField]
private float m_ValueX = 1f; private float m_ValueX = 1f;
public float ValueX public float ValueX
{ {
get get
{ {
if (WholeNumbers) if (WholeNumbers)
return Mathf.Round(m_ValueX); return Mathf.Round(m_ValueX);
return m_ValueX; return m_ValueX;
} }
set set
{ {
SetX(value); SetX(value);
} }
} }
public float NormalizedValueX public float NormalizedValueX
{ {
get get
{ {
if (Mathf.Approximately(MinValue, MaxValue)) if (Mathf.Approximately(MinValue, MaxValue))
return 0; return 0;
return Mathf.InverseLerp(MinValue, MaxValue, ValueX); return Mathf.InverseLerp(MinValue, MaxValue, ValueX);
} }
set set
{ {
this.ValueX = Mathf.Lerp(MinValue, MaxValue, value); this.ValueX = Mathf.Lerp(MinValue, MaxValue, value);
} }
} }
[SerializeField] [SerializeField]
private float m_ValueY = 1f; private float m_ValueY = 1f;
public float ValueY public float ValueY
{ {
get get
{ {
if (WholeNumbers) if (WholeNumbers)
return Mathf.Round(m_ValueY); return Mathf.Round(m_ValueY);
return m_ValueY; return m_ValueY;
} }
set set
{ {
SetY(value); SetY(value);
} }
} }
public float NormalizedValueY public float NormalizedValueY
{ {
get get
{ {
if (Mathf.Approximately(MinValue, MaxValue)) if (Mathf.Approximately(MinValue, MaxValue))
return 0; return 0;
return Mathf.InverseLerp(MinValue, MaxValue, ValueY); return Mathf.InverseLerp(MinValue, MaxValue, ValueY);
} }
set set
{ {
this.ValueY = Mathf.Lerp(MinValue, MaxValue, value); this.ValueY = Mathf.Lerp(MinValue, MaxValue, value);
} }
} }
[Space(6)] [Space(6)]
// Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers. // Allow for delegate-based subscriptions for faster events than 'eventReceiver', and allowing for multiple receivers.
[SerializeField] [SerializeField]
private BoxSliderEvent m_OnValueChanged = new BoxSliderEvent(); private BoxSliderEvent m_OnValueChanged = new BoxSliderEvent();
public BoxSliderEvent OnValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } } public BoxSliderEvent OnValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } }
// Private fields // Private fields
private Transform m_HandleTransform; private Transform m_HandleTransform;
private RectTransform m_HandleContainerRect; private RectTransform m_HandleContainerRect;
// The offset from handle position to mouse down position // The offset from handle position to mouse down position
private Vector2 m_Offset = Vector2.zero; private Vector2 m_Offset = Vector2.zero;
private DrivenRectTransformTracker m_Tracker; private DrivenRectTransformTracker m_Tracker;
// Size of each step. // Size of each step.
float StepSize { get { return WholeNumbers ? 1 : (MaxValue - MinValue) * 0.1f; } } float StepSize { get { return WholeNumbers ? 1 : (MaxValue - MinValue) * 0.1f; } }
protected BoxSlider() protected BoxSlider()
{ } { }
#if UNITY_EDITOR #if UNITY_EDITOR
protected override void OnValidate() protected override void OnValidate()
{ {
base.OnValidate(); base.OnValidate();
if (WholeNumbers) if (WholeNumbers)
{ {
m_MinValue = Mathf.Round(m_MinValue); m_MinValue = Mathf.Round(m_MinValue);
m_MaxValue = Mathf.Round(m_MaxValue); m_MaxValue = Mathf.Round(m_MaxValue);
} }
UpdateCachedReferences(); UpdateCachedReferences();
SetX(m_ValueX, false); SetX(m_ValueX, false);
SetY(m_ValueY, false); SetY(m_ValueY, false);
// Update rects since other things might affect them even if value didn't change. // Update rects since other things might affect them even if value didn't change.
if(!Application.isPlaying) UpdateVisuals(); if(!Application.isPlaying) UpdateVisuals();
#if UNITY_2018_3_OR_NEWER #if UNITY_2018_3_OR_NEWER
if (!Application.isPlaying) if (!Application.isPlaying)
#else #else
var prefabType = UnityEditor.PrefabUtility.GetPrefabType(this); var prefabType = UnityEditor.PrefabUtility.GetPrefabType(this);
if (prefabType != UnityEditor.PrefabType.Prefab && !Application.isPlaying) if (prefabType != UnityEditor.PrefabType.Prefab && !Application.isPlaying)
#endif #endif
{ {
CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this);
} }
} }
#endif // if UNITY_EDITOR #endif // if UNITY_EDITOR
public virtual void Rebuild(CanvasUpdate executing) public virtual void Rebuild(CanvasUpdate executing)
{ {
#if UNITY_EDITOR #if UNITY_EDITOR
if (executing == CanvasUpdate.Prelayout) if (executing == CanvasUpdate.Prelayout)
OnValueChanged.Invoke(ValueX, ValueY); OnValueChanged.Invoke(ValueX, ValueY);
#endif #endif
} }
public void LayoutComplete() public void LayoutComplete()
{ {
} }
public void GraphicUpdateComplete() public void GraphicUpdateComplete()
{ {
} }
public static bool SetClass<T>(ref T currentValue, T newValue) where T : class public static bool SetClass<T>(ref T currentValue, T newValue) where T : class
{ {
if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue))) if ((currentValue == null && newValue == null) || (currentValue != null && currentValue.Equals(newValue)))
return false; return false;
currentValue = newValue; currentValue = newValue;
return true; return true;
} }
public static bool SetStruct<T>(ref T currentValue, T newValue) where T : struct public static bool SetStruct<T>(ref T currentValue, T newValue) where T : struct
{ {
if (currentValue.Equals(newValue)) if (currentValue.Equals(newValue))
return false; return false;
currentValue = newValue; currentValue = newValue;
return true; return true;
} }
protected override void OnEnable() protected override void OnEnable()
{ {
base.OnEnable(); base.OnEnable();
UpdateCachedReferences(); UpdateCachedReferences();
SetX(m_ValueX, false); SetX(m_ValueX, false);
SetY(m_ValueY, false); SetY(m_ValueY, false);
// Update rects since they need to be initialized correctly. // Update rects since they need to be initialized correctly.
UpdateVisuals(); UpdateVisuals();
} }
protected override void OnDisable() protected override void OnDisable()
{ {
m_Tracker.Clear(); m_Tracker.Clear();
base.OnDisable(); base.OnDisable();
} }
void UpdateCachedReferences() void UpdateCachedReferences()
{ {
if (m_HandleRect) if (m_HandleRect)
{ {
m_HandleTransform = m_HandleRect.transform; m_HandleTransform = m_HandleRect.transform;
if (m_HandleTransform.parent != null) if (m_HandleTransform.parent != null)
m_HandleContainerRect = m_HandleTransform.parent.GetComponent<RectTransform>(); m_HandleContainerRect = m_HandleTransform.parent.GetComponent<RectTransform>();
} }
else else
{ {
m_HandleContainerRect = null; m_HandleContainerRect = null;
} }
} }
// Set the valueUpdate the visible Image. // Set the valueUpdate the visible Image.
void SetX(float input) void SetX(float input)
{ {
SetX(input, true); SetX(input, true);
} }
void SetX(float input, bool sendCallback) void SetX(float input, bool sendCallback)
{ {
// Clamp the input // Clamp the input
float newValue = Mathf.Clamp(input, MinValue, MaxValue); float newValue = Mathf.Clamp(input, MinValue, MaxValue);
if (WholeNumbers) if (WholeNumbers)
newValue = Mathf.Round(newValue); newValue = Mathf.Round(newValue);
// If the stepped value doesn't match the last one, it's time to update // If the stepped value doesn't match the last one, it's time to update
if (m_ValueX == newValue) if (m_ValueX == newValue)
return; return;
m_ValueX = newValue; m_ValueX = newValue;
UpdateVisuals(); UpdateVisuals();
if (sendCallback) if (sendCallback)
m_OnValueChanged.Invoke(newValue, ValueY); m_OnValueChanged.Invoke(newValue, ValueY);
} }
void SetY(float input) void SetY(float input)
{ {
SetY(input, true); SetY(input, true);
} }
void SetY(float input, bool sendCallback) void SetY(float input, bool sendCallback)
{ {
// Clamp the input // Clamp the input
float newValue = Mathf.Clamp(input, MinValue, MaxValue); float newValue = Mathf.Clamp(input, MinValue, MaxValue);
if (WholeNumbers) if (WholeNumbers)
newValue = Mathf.Round(newValue); newValue = Mathf.Round(newValue);
// If the stepped value doesn't match the last one, it's time to update // If the stepped value doesn't match the last one, it's time to update
if (m_ValueY == newValue) if (m_ValueY == newValue)
return; return;
m_ValueY = newValue; m_ValueY = newValue;
UpdateVisuals(); UpdateVisuals();
if (sendCallback) if (sendCallback)
m_OnValueChanged.Invoke(ValueX, newValue); m_OnValueChanged.Invoke(ValueX, newValue);
} }
protected override void OnRectTransformDimensionsChange() protected override void OnRectTransformDimensionsChange()
{ {
base.OnRectTransformDimensionsChange(); base.OnRectTransformDimensionsChange();
UpdateVisuals(); UpdateVisuals();
} }
enum Axis enum Axis
{ {
Horizontal = 0, Horizontal = 0,
Vertical = 1 Vertical = 1
} }
// Force-update the slider. Useful if you've changed the properties and want it to update visually. // Force-update the slider. Useful if you've changed the properties and want it to update visually.
private void UpdateVisuals() private void UpdateVisuals()
{ {
#if UNITY_EDITOR #if UNITY_EDITOR
if (!Application.isPlaying) if (!Application.isPlaying)
UpdateCachedReferences(); UpdateCachedReferences();
#endif #endif
m_Tracker.Clear(); m_Tracker.Clear();
//to business! //to business!
if (m_HandleContainerRect != null) if (m_HandleContainerRect != null)
{ {
m_Tracker.Add(this, m_HandleRect, DrivenTransformProperties.Anchors); m_Tracker.Add(this, m_HandleRect, DrivenTransformProperties.Anchors);
Vector2 anchorMin = Vector2.zero; Vector2 anchorMin = Vector2.zero;
Vector2 anchorMax = Vector2.one; Vector2 anchorMax = Vector2.one;
anchorMin[0] = anchorMax[0] = (NormalizedValueX); anchorMin[0] = anchorMax[0] = (NormalizedValueX);
anchorMin[1] = anchorMax[1] = (NormalizedValueY); anchorMin[1] = anchorMax[1] = (NormalizedValueY);
if (Application.isPlaying) if (Application.isPlaying)
{ {
m_HandleRect.anchorMin = anchorMin; m_HandleRect.anchorMin = anchorMin;
m_HandleRect.anchorMax = anchorMax; m_HandleRect.anchorMax = anchorMax;
} }
} }
} }
// Update the slider's position based on the mouse. // Update the slider's position based on the mouse.
void UpdateDrag(PointerEventData eventData, Camera cam) void UpdateDrag(PointerEventData eventData, Camera cam)
{ {
RectTransform clickRect = m_HandleContainerRect; RectTransform clickRect = m_HandleContainerRect;
if (clickRect != null && clickRect.rect.size[0] > 0) if (clickRect != null && clickRect.rect.size[0] > 0)
{ {
Vector2 localCursor; Vector2 localCursor;
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, eventData.position, cam, out localCursor)) if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(clickRect, eventData.position, cam, out localCursor))
return; return;
localCursor -= clickRect.rect.position; localCursor -= clickRect.rect.position;
float val = Mathf.Clamp01((localCursor - m_Offset)[0] / clickRect.rect.size[0]); float val = Mathf.Clamp01((localCursor - m_Offset)[0] / clickRect.rect.size[0]);
NormalizedValueX = (val); NormalizedValueX = (val);
float valY = Mathf.Clamp01((localCursor - m_Offset)[1] / clickRect.rect.size[1]); float valY = Mathf.Clamp01((localCursor - m_Offset)[1] / clickRect.rect.size[1]);
NormalizedValueY = (valY); NormalizedValueY = (valY);
} }
} }
private bool CanDrag(PointerEventData eventData) private bool CanDrag(PointerEventData eventData)
{ {
return IsActive() && IsInteractable() && eventData.button == PointerEventData.InputButton.Left; return IsActive() && IsInteractable() && eventData.button == PointerEventData.InputButton.Left;
} }
public override void OnPointerDown(PointerEventData eventData) public override void OnPointerDown(PointerEventData eventData)
{ {
if (!CanDrag(eventData)) if (!CanDrag(eventData))
return; return;
base.OnPointerDown(eventData); base.OnPointerDown(eventData);
m_Offset = Vector2.zero; m_Offset = Vector2.zero;
if (m_HandleContainerRect != null && RectTransformUtility.RectangleContainsScreenPoint(m_HandleRect, eventData.position, eventData.enterEventCamera)) if (m_HandleContainerRect != null && RectTransformUtility.RectangleContainsScreenPoint(m_HandleRect, eventData.position, eventData.enterEventCamera))
{ {
Vector2 localMousePos; Vector2 localMousePos;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HandleRect, eventData.position, eventData.pressEventCamera, out localMousePos)) if (RectTransformUtility.ScreenPointToLocalPointInRectangle(m_HandleRect, eventData.position, eventData.pressEventCamera, out localMousePos))
m_Offset = localMousePos; m_Offset = localMousePos;
m_Offset.y = -m_Offset.y; m_Offset.y = -m_Offset.y;
} }
else else
{ {
// Outside the slider handle - jump to this point instead // Outside the slider handle - jump to this point instead
UpdateDrag(eventData, eventData.pressEventCamera); UpdateDrag(eventData, eventData.pressEventCamera);
} }
} }
public virtual void OnDrag(PointerEventData eventData) public virtual void OnDrag(PointerEventData eventData)
{ {
if (!CanDrag(eventData)) if (!CanDrag(eventData))
return; return;
UpdateDrag(eventData, eventData.pressEventCamera); UpdateDrag(eventData, eventData.pressEventCamera);
} }
public virtual void OnInitializePotentialDrag(PointerEventData eventData) public virtual void OnInitializePotentialDrag(PointerEventData eventData)
{ {
eventData.useDragThreshold = false; eventData.useDragThreshold = false;
} }
} }
} }

View File

@ -0,0 +1,347 @@
///Credit brogan89
///Sourced from - https://github.com/brogan89/MinMaxSlider
using System;
using TMPro;
using UnityEngine.Events;
using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions
{
[RequireComponent(typeof(RectTransform))]
[AddComponentMenu("UI/Extensions/Sliders/MinMax Slider")]
public class MinMaxSlider : Selectable, IBeginDragHandler, IDragHandler, IEndDragHandler
{
private enum DragState
{
Both,
Min,
Max
}
[Header("UI Controls")]
[SerializeField] private Camera customCamera = null;
[SerializeField] private RectTransform sliderBounds = null;
[SerializeField] private RectTransform minHandle = null;
[SerializeField] private RectTransform maxHandle = null;
[SerializeField] private RectTransform middleGraphic = null;
// text components (optional)
[Header("Display Text (Optional)")]
[SerializeField] private TextMeshProUGUI minText = null;
[SerializeField] private TextMeshProUGUI maxText = null;
[SerializeField] private string textFormat = "0";
// values
[Header("Limits")]
[SerializeField] private float minLimit = 0;
[SerializeField] private float maxLimit = 100;
[Header("Values")]
public bool wholeNumbers;
[SerializeField] private float minValue = 25;
[SerializeField] private float maxValue = 75;
public MinMaxValues Values => new MinMaxValues(minValue, maxValue, minLimit, maxLimit);
public RectTransform SliderBounds { get => sliderBounds; set => sliderBounds = value; }
public RectTransform MinHandle { get => minHandle; set => minHandle = value; }
public RectTransform MaxHandle { get => maxHandle; set => maxHandle = value; }
public RectTransform MiddleGraphic { get => middleGraphic; set => middleGraphic = value; }
public TextMeshProUGUI MinText { get => minText; set => minText = value; }
public TextMeshProUGUI MaxText { get => maxText; set => maxText = value; }
/// <summary>
/// Event invoked when either slider value has changed
/// <para></para>
/// T0 = min, T1 = max
/// </summary>
[Serializable]
public class SliderEvent : UnityEvent<float, float> { }
public SliderEvent onValueChanged = new SliderEvent();
private Vector2 dragStartPosition;
private float dragStartMinValue01;
private float dragStartMaxValue01;
private DragState dragState;
private bool passDragEvents; // this allows drag events to be passed through to scrollers
private Camera mainCamera;
private Canvas parentCanvas;
private bool isOverlayCanvas;
protected override void Start()
{
base.Start();
if (!sliderBounds)
{
sliderBounds = transform as RectTransform;
}
parentCanvas = GetComponentInParent<Canvas>();
isOverlayCanvas = parentCanvas.renderMode == RenderMode.ScreenSpaceOverlay;
mainCamera = customCamera != null ? customCamera : Camera.main;
}
public void SetLimits(float minLimit, float maxLimit)
{
this.minLimit = wholeNumbers ? Mathf.RoundToInt(minLimit) : minLimit;
this.maxLimit = wholeNumbers ? Mathf.RoundToInt(maxLimit) : maxLimit;
}
public void SetValues(MinMaxValues values, bool notify = true)
{
SetValues(values.minValue, values.maxValue, values.minLimit, values.maxLimit, notify);
}
public void SetValues(float minValue, float maxValue, bool notify = true)
{
SetValues(minValue, maxValue, minLimit, maxLimit, notify);
}
public void SetValues(float minValue, float maxValue, float minLimit, float maxLimit, bool notify = true)
{
this.minValue = wholeNumbers ? Mathf.RoundToInt(minValue) : minValue;
this.maxValue = wholeNumbers ? Mathf.RoundToInt(maxValue) : maxValue;
SetLimits(minLimit, maxLimit);
RefreshSliders();
UpdateText();
UpdateMiddleGraphic();
if (notify)
{
// event
onValueChanged.Invoke(this.minValue, this.maxValue);
}
}
private void RefreshSliders()
{
SetSliderAnchors();
float clampedMin = Mathf.Clamp(minValue, minLimit, maxLimit);
SetMinHandleValue01(minHandle, GetPercentage(minLimit, maxLimit, clampedMin));
float clampedMax = Mathf.Clamp(maxValue, minLimit, maxLimit);
SetMaxHandleValue01(maxHandle, GetPercentage(minLimit, maxLimit, clampedMax));
}
private void SetSliderAnchors()
{
minHandle.anchorMin = new Vector2(0, 0.5f);
minHandle.anchorMax = new Vector2(0, 0.5f);
minHandle.pivot = new Vector2(0.5f, 0.5f);
maxHandle.anchorMin = new Vector2(1, 0.5f);
maxHandle.anchorMax = new Vector2(1, 0.5f);
maxHandle.pivot = new Vector2(0.5f, 0.5f);
}
private void UpdateText()
{
if (minText)
{
minText.SetText(minValue.ToString(textFormat));
}
if (maxText)
{
maxText.SetText(maxValue.ToString(textFormat));
}
}
private void UpdateMiddleGraphic()
{
if (!middleGraphic) return;
middleGraphic.anchorMin = Vector2.zero;
middleGraphic.anchorMax = Vector2.one;
middleGraphic.offsetMin = new Vector2(minHandle.anchoredPosition.x, 0);
middleGraphic.offsetMax = new Vector2(maxHandle.anchoredPosition.x, 0);
}
#region IDragHandler
public void OnBeginDrag(PointerEventData eventData)
{
passDragEvents = Math.Abs(eventData.delta.x) < Math.Abs(eventData.delta.y);
if (passDragEvents)
{
PassDragEvents<IBeginDragHandler>(x => x.OnBeginDrag(eventData));
}
else
{
Camera uiCamera = isOverlayCanvas ? null : mainCamera;
RectTransformUtility.ScreenPointToLocalPointInRectangle(sliderBounds, eventData.position, uiCamera, out dragStartPosition);
float dragStartValue = GetValueOfPointInSliderBounds01(dragStartPosition);
dragStartMinValue01 = GetMinHandleValue01(minHandle);
dragStartMaxValue01 = GetMaxHandleValue01(maxHandle);
// set drag state
if (dragStartValue < dragStartMinValue01 || RectTransformUtility.RectangleContainsScreenPoint(minHandle, eventData.position, uiCamera))
{
dragState = DragState.Min;
minHandle.SetAsLastSibling();
}
else if (dragStartValue > dragStartMaxValue01 || RectTransformUtility.RectangleContainsScreenPoint(maxHandle, eventData.position, uiCamera))
{
dragState = DragState.Max;
maxHandle.SetAsLastSibling();
}
else
{
dragState = DragState.Both;
}
}
}
public void OnDrag(PointerEventData eventData)
{
if (passDragEvents)
{
PassDragEvents<IDragHandler>(x => x.OnDrag(eventData));
}
else if (minHandle && maxHandle)
{
RectTransformUtility.ScreenPointToLocalPointInRectangle(sliderBounds, eventData.position, isOverlayCanvas ? null : mainCamera, out Vector2 clickPosition);
SetSliderAnchors();
if (dragState == DragState.Min || dragState == DragState.Max)
{
float dragPosition01 = GetValueOfPointInSliderBounds01(clickPosition);
float minHandleValue = GetMinHandleValue01(minHandle);
float maxHandleValue = GetMaxHandleValue01(maxHandle);
if (dragState == DragState.Min)
SetMinHandleValue01(minHandle, Mathf.Clamp(dragPosition01, 0, maxHandleValue));
else if (dragState == DragState.Max)
SetMaxHandleValue01(maxHandle, Mathf.Clamp(dragPosition01, minHandleValue, 1));
}
else
{
float distancePercent = (clickPosition.x - dragStartPosition.x) / sliderBounds.rect.width;
SetMinHandleValue01(minHandle, dragStartMinValue01 + distancePercent);
SetMaxHandleValue01(maxHandle, dragStartMaxValue01 + distancePercent);
}
// set values
float min = Mathf.Lerp(minLimit, maxLimit, GetMinHandleValue01(minHandle));
float max = Mathf.Lerp(minLimit, maxLimit, GetMaxHandleValue01(maxHandle));
SetValues(min, max);
UpdateText();
UpdateMiddleGraphic();
}
}
public void OnEndDrag(PointerEventData eventData)
{
if (passDragEvents)
{
PassDragEvents<IEndDragHandler>(x => x.OnEndDrag(eventData));
}
else
{
float minHandleValue = GetMinHandleValue01(minHandle);
float maxHandleValue = GetMaxHandleValue01(maxHandle);
// this safe guards a possible situation where the slides can get stuck
if (Math.Abs(minHandleValue) < MinMaxValues.FLOAT_TOL && Math.Abs(maxHandleValue) < MinMaxValues.FLOAT_TOL)
{
maxHandle.SetAsLastSibling();
}
else if (Math.Abs(minHandleValue - 1) < MinMaxValues.FLOAT_TOL && Math.Abs(maxHandleValue - 1) < MinMaxValues.FLOAT_TOL)
{
minHandle.SetAsLastSibling();
}
}
}
#endregion IDragHandler
private void PassDragEvents<T>(Action<T> callback) where T : IEventSystemHandler
{
Transform parent = transform.parent;
while (parent != null)
{
foreach (var component in parent.GetComponents<Component>())
{
if (!(component is T)) continue;
callback.Invoke((T)(IEventSystemHandler)component);
return;
}
parent = parent.parent;
}
}
/// <summary>
/// Sets position of max handle RectTransform
/// </summary>
/// <param name="handle"></param>
/// <param name="value01">Normalized handle position</param>
private void SetMaxHandleValue01(RectTransform handle, float value01)
{
handle.anchoredPosition = new Vector2(value01 * sliderBounds.rect.width - sliderBounds.rect.width + sliderBounds.offsetMax.x, handle.anchoredPosition.y);
}
/// <summary>
/// Sets position of min handle RectTransform
/// </summary>
/// <param name="handle"></param>
/// <param name="value01">Normalized handle position</param>
private void SetMinHandleValue01(RectTransform handle, float value01)
{
handle.anchoredPosition = new Vector2(value01 * sliderBounds.rect.width + sliderBounds.offsetMin.x, handle.anchoredPosition.y);
}
/// <summary>
/// Returns normalized position of max handle RectTransform
/// </summary>
/// <param name="handle"></param>
/// <returns>Normalized position of max handle RectTransform</returns>
private float GetMaxHandleValue01(RectTransform handle)
{
return 1 + (handle.anchoredPosition.x - sliderBounds.offsetMax.x) / sliderBounds.rect.width;
}
/// <summary>
/// Returns normalized position of min handle RectTransform
/// </summary>
/// <param name="handle"></param>
/// <returns>Normalized position of min handle RectTransform</returns>
private float GetMinHandleValue01(RectTransform handle)
{
return (handle.anchoredPosition.x - sliderBounds.offsetMin.x) / sliderBounds.rect.width;
}
/// <summary>
/// Returns normalized position of a point in a slider bounds rectangle
/// </summary>
/// <param name="position"></param>
/// <returns>Normalized position of a point in a slider bounds rectangle</returns>
private float GetValueOfPointInSliderBounds01(Vector2 position)
{
var width = sliderBounds.rect.width;
return Mathf.Clamp((position.x + width / 2) / width, 0, 1);
}
/// <summary>
/// Returns percentage of input based on min and max values
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
/// <param name="input"></param>
/// <returns></returns>
private static float GetPercentage(float min, float max, float input)
{
return (input - min) / (max - min);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b9f9954231c8bab419504a7ac5ff133e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -8,8 +8,8 @@ using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[AddComponentMenu("UI/Extensions/Radial Slider")]
[RequireComponent(typeof(Image))] [RequireComponent(typeof(Image))]
[AddComponentMenu("UI/Extensions/Sliders/Radial Slider")]
public class RadialSlider : MonoBehaviour, IPointerEnterHandler, IPointerDownHandler, IPointerUpHandler, IDragHandler public class RadialSlider : MonoBehaviour, IPointerEnterHandler, IPointerDownHandler, IPointerUpHandler, IDragHandler
{ {
private bool isPointerDown, isPointerReleased, lerpInProgress; private bool isPointerDown, isPointerReleased, lerpInProgress;

View File

@ -9,9 +9,9 @@ using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[AddComponentMenu("UI/Extensions/Range Slider", 34)]
[ExecuteInEditMode] [ExecuteInEditMode]
[RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(RectTransform))]
[AddComponentMenu("UI/Extensions/Sliders/Range Slider", 34)]
public class RangeSlider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement public class RangeSlider : Selectable, IDragHandler, IInitializePotentialDragHandler, ICanvasElement
{ {
public enum Direction public enum Direction

View File

@ -9,9 +9,8 @@ using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
// Stepper control // Stepper control
[ExecuteInEditMode]
[AddComponentMenu("UI/Extensions/Stepper")]
[RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(RectTransform))]
[AddComponentMenu("UI/Extensions/Sliders/Stepper")]
public class Stepper : UIBehaviour public class Stepper : UIBehaviour
{ {
private Selectable[] _sides; private Selectable[] _sides;
@ -90,6 +89,28 @@ namespace UnityEngine.UI.Extensions
protected Stepper() protected Stepper()
{ } { }
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
RecreateSprites(sides);
if (separator)
LayoutSides();
if (!wrap)
{
DisableAtExtremes(sides);
}
}
#endif
protected override void Start()
{
if (isActiveAndEnabled)
StartCoroutine(DelayedInit());
}
protected override void OnEnable() protected override void OnEnable()
{ {
StartCoroutine(DelayedInit()); StartCoroutine(DelayedInit());

View File

@ -16,7 +16,12 @@ namespace UnityEngine.UI.Extensions {
[AddComponentMenu("UI/Extensions/TextPic")] [AddComponentMenu("UI/Extensions/TextPic")]
[ExecuteInEditMode] // Needed for culling images that are not used // [ExecuteInEditMode] // Needed for culling images that are not used //
public class TextPic : Text, IPointerClickHandler, IPointerExitHandler, IPointerEnterHandler, ISelectHandler { #if UNITY_2022_1_OR_NEWER
public class TextPic : TMPro.TMP_Text, IPointerClickHandler, IPointerExitHandler, IPointerEnterHandler, ISelectHandler
#else
public class TextPic : Text, IPointerClickHandler, IPointerExitHandler, IPointerEnterHandler, ISelectHandler
#endif
{
// Icon entry to replace text with // Icon entry to replace text with
[Serializable] [Serializable]
public struct IconName { public struct IconName {
@ -465,14 +470,21 @@ namespace UnityEngine.UI.Extensions {
/// UNITY METHODS /// /// UNITY METHODS ///
protected override void OnPopulateMesh(VertexHelper toFill) { protected override void OnPopulateMesh(VertexHelper toFill) {
#if UNITY_2022_1_OR_NEWER
originalText = text;
text = GetOutputText();
base.OnPopulateMesh(toFill);
text = originalText;
#else
originalText = m_Text; originalText = m_Text;
m_Text = GetOutputText(); m_Text = GetOutputText();
base.OnPopulateMesh(toFill); base.OnPopulateMesh(toFill);
m_DisableFontTextureRebuiltCallback = true; m_DisableFontTextureRebuiltCallback = true;
m_Text = originalText; m_Text = originalText;
#endif
positions.Clear(); positions.Clear();
@ -539,8 +551,10 @@ namespace UnityEngine.UI.Extensions {
// Update the quad images // Update the quad images
updateQuad = true; updateQuad = true;
#if !UNITY_2022_1_OR_NEWER
m_DisableFontTextureRebuiltCallback = false; m_DisableFontTextureRebuiltCallback = false;
} #endif
}
/// <summary> /// <summary>
/// Click event is detected whether to click a hyperlink text /// Click event is detected whether to click a hyperlink text
@ -641,7 +655,7 @@ namespace UnityEngine.UI.Extensions {
#endif #endif
protected override void OnEnable() { protected override void OnEnable() {
#if UNITY_2019_1_OR_NEWER #if UNITY_2019_1_OR_NEWER
// Here is the hack to see if Unity is using the new rendering system for text // Here is the hack to see if Unity is using the new rendering system for text
usesNewRendering = false; usesNewRendering = false;
@ -660,13 +674,14 @@ namespace UnityEngine.UI.Extensions {
else { else {
usesNewRendering = true; usesNewRendering = true;
} }
#endif #endif
base.OnEnable(); base.OnEnable();
#if !UNITY_2022_1_OR_NEWER
supportRichText = true; supportRichText = true;
alignByGeometry = true; alignByGeometry = true;
#endif
// Enable images on TextPic disable // Enable images on TextPic disable
if (m_ImagesPool.Count >= 1) { if (m_ImagesPool.Count >= 1) {
for (int i = 0; i < m_ImagesPool.Count; i++) { for (int i = 0; i < m_ImagesPool.Count; i++) {

View File

@ -1,6 +1,8 @@
/// Credit Melang /// Credit Melang
/// Sourced from - http://forum.unity3d.com/members/melang.593409/ /// Sourced from - http://forum.unity3d.com/members/melang.593409/
/// NOT supported in Unity 2022
#if !UNITY_2022_1_OR_NEWER
using System.Collections.Generic; using System.Collections.Generic;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
@ -60,3 +62,4 @@ namespace UnityEngine.UI.Extensions
} }
} }
} }
#endif

View File

@ -1,6 +1,7 @@
/// Credit Titinious (https://github.com/Titinious) /// Credit Titinious (https://github.com/Titinious)
/// Sourced from - https://github.com/Titinious/CurlyUI /// Sourced from - https://github.com/Titinious/CurlyUI
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
#if UNITY_EDITOR #if UNITY_EDITOR
@ -284,7 +285,9 @@ namespace UnityEngine.UI.Extensions
} }
} }
public void Refresh() public void Refresh() { Invoke(nameof(Refreshx), 0.3f); }
private void Refreshx()
{ {
ReportSet(); ReportSet();

View File

@ -4,14 +4,24 @@
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(RectTransform))]
#if UNITY_2022_1_OR_NEWER
[RequireComponent(typeof(TMPro.TMP_Text))]
#else
[RequireComponent(typeof(Text))] [RequireComponent(typeof(Text))]
#endif
[AddComponentMenu("UI/Effects/Extensions/Curly UI Text")] [AddComponentMenu("UI/Effects/Extensions/Curly UI Text")]
public class CUIText : CUIGraphic public class CUIText : CUIGraphic
{ {
public override void ReportSet() public override void ReportSet()
{ {
if (uiGraphic == null) if (uiGraphic == null)
{
#if UNITY_2022_1_OR_NEWER
uiGraphic = GetComponent<TMPro.TMP_Text>();
#else
uiGraphic = GetComponent<Text>(); uiGraphic = GetComponent<Text>();
#endif
}
base.ReportSet(); base.ReportSet();
} }

View File

@ -3,7 +3,12 @@
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[RequireComponent(typeof(Text), typeof(RectTransform))] #if UNITY_2022_1_OR_NEWER
[RequireComponent(typeof(TMPro.TMP_Text))]
#else
[RequireComponent(typeof(Text))]
#endif
[RequireComponent(typeof(RectTransform))]
[AddComponentMenu("UI/Effects/Extensions/Curved Text")] [AddComponentMenu("UI/Effects/Extensions/Curved Text")]
public class CurvedText : BaseMeshEffect public class CurvedText : BaseMeshEffect
{ {

View File

@ -4,7 +4,12 @@
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[RequireComponent(typeof(Text), typeof(RectTransform))] #if UNITY_2022_1_OR_NEWER
[RequireComponent(typeof(TMPro.TMP_Text))]
#else
[RequireComponent(typeof(Text))]
#endif
[RequireComponent(typeof(RectTransform))]
[AddComponentMenu("UI/Effects/Extensions/Cylinder Text")] [AddComponentMenu("UI/Effects/Extensions/Cylinder Text")]
public class CylinderText : BaseMeshEffect public class CylinderText : BaseMeshEffect
{ {

View File

@ -81,8 +81,12 @@ namespace UnityEngine.UI.Extensions
List<UIVertex> verts = new List<UIVertex>(); List<UIVertex> verts = new List<UIVertex>();
vh.GetUIVertexStream(verts); vh.GetUIVertexStream(verts);
Text text = GetComponent<Text>(); #if UNITY_2022_1_OR_NEWER
if (text == null) var text = GetComponent<TMPro.TMP_Text>();
#else
var text = GetComponent<Text>();
#endif
if (text == null)
{ {
Debug.LogWarning("LetterSpacing: Missing Text component"); Debug.LogWarning("LetterSpacing: Missing Text component");
return; return;
@ -93,29 +97,52 @@ namespace UnityEngine.UI.Extensions
float letterOffset = spacing * (float)text.fontSize / 100f; float letterOffset = spacing * (float)text.fontSize / 100f;
float alignmentFactor = 0; float alignmentFactor = 0;
int glyphIdx = 0; int glyphIdx = 0;
switch (text.alignment) #if UNITY_2022_1_OR_NEWER
{ switch (text.alignment)
case TextAnchor.LowerLeft: {
case TextAnchor.MiddleLeft: case TMPro.TextAlignmentOptions.BottomLeft:
case TextAnchor.UpperLeft: case TMPro.TextAlignmentOptions.MidlineLeft:
alignmentFactor = 0f; case TMPro.TextAlignmentOptions.TopLeft:
break; alignmentFactor = 0f;
break;
case TextAnchor.LowerCenter:
case TextAnchor.MiddleCenter: case TMPro.TextAlignmentOptions.BottomJustified:
case TextAnchor.UpperCenter: case TMPro.TextAlignmentOptions.MidlineJustified:
alignmentFactor = 0.5f; case TMPro.TextAlignmentOptions.TopJustified:
break; alignmentFactor = 0.5f;
break;
case TextAnchor.LowerRight:
case TextAnchor.MiddleRight: case TMPro.TextAlignmentOptions.BottomRight:
case TextAnchor.UpperRight: case TMPro.TextAlignmentOptions.MidlineRight:
alignmentFactor = 1f; case TMPro.TextAlignmentOptions.TopRight:
break; alignmentFactor = 1f;
} break;
}
for (int lineIdx=0; lineIdx < lines.Length; lineIdx++) #else
switch (text.alignment)
{
case TextAnchor.LowerLeft:
case TextAnchor.MiddleLeft:
case TextAnchor.UpperLeft:
alignmentFactor = 0f;
break;
case TextAnchor.LowerCenter:
case TextAnchor.MiddleCenter:
case TextAnchor.UpperCenter:
alignmentFactor = 0.5f;
break;
case TextAnchor.LowerRight:
case TextAnchor.MiddleRight:
case TextAnchor.UpperRight:
alignmentFactor = 1f;
break;
}
#endif
for (int lineIdx=0; lineIdx < lines.Length; lineIdx++)
{ {
string line = lines[lineIdx]; string line = lines[lineIdx];
float lineOffset = (line.Length -1) * letterOffset * alignmentFactor; float lineOffset = (line.Length -1) * letterOffset * alignmentFactor;

View File

@ -46,7 +46,11 @@ using System.Collections.Generic;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[AddComponentMenu("UI/Effects/Extensions/Mono Spacing")] [AddComponentMenu("UI/Effects/Extensions/Mono Spacing")]
#if UNITY_2022_1_OR_NEWER
[RequireComponent(typeof(TMPro.TMP_Text))]
#else
[RequireComponent(typeof(Text))] [RequireComponent(typeof(Text))]
#endif
[RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(RectTransform))]
///Summary ///Summary
/// Note, Vertex Count has changed in 5.2.1+, is now 6 (two tris) instead of 4 (tri strip). /// Note, Vertex Count has changed in 5.2.1+, is now 6 (two tris) instead of 4 (tri strip).
@ -58,13 +62,21 @@ namespace UnityEngine.UI.Extensions
public bool UseHalfCharWidth = false; public bool UseHalfCharWidth = false;
private RectTransform rectTransform; private RectTransform rectTransform;
#if UNITY_2022_1_OR_NEWER
private TMPro.TMP_Text text;
#else
private Text text; private Text text;
#endif
protected MonoSpacing() { } protected MonoSpacing() { }
protected override void Awake() protected override void Awake()
{ {
#if UNITY_2022_1_OR_NEWER
text = GetComponent<TMPro.TMP_Text>();
#else
text = GetComponent<Text>(); text = GetComponent<Text>();
#endif
if (text == null) if (text == null)
{ {
Debug.LogWarning("MonoSpacing: Missing Text component"); Debug.LogWarning("MonoSpacing: Missing Text component");
@ -104,29 +116,51 @@ namespace UnityEngine.UI.Extensions
float letterOffset = Spacing * (float)text.fontSize / 100f; float letterOffset = Spacing * (float)text.fontSize / 100f;
float alignmentFactor = 0; float alignmentFactor = 0;
int glyphIdx = 0; int glyphIdx = 0;
switch (text.alignment) #if UNITY_2022_1_OR_NEWER
switch (text.alignment)
{ {
case TextAnchor.LowerLeft: case TMPro.TextAlignmentOptions.BottomLeft:
case TextAnchor.MiddleLeft: case TMPro.TextAlignmentOptions.MidlineLeft:
case TextAnchor.UpperLeft: case TMPro.TextAlignmentOptions.TopLeft:
alignmentFactor = 0f; alignmentFactor = 0f;
break; break;
case TextAnchor.LowerCenter: case TMPro.TextAlignmentOptions.BottomJustified:
case TextAnchor.MiddleCenter: case TMPro.TextAlignmentOptions.MidlineJustified:
case TextAnchor.UpperCenter: case TMPro.TextAlignmentOptions.TopJustified:
alignmentFactor = 0.5f; alignmentFactor = 0.5f;
break; break;
case TextAnchor.LowerRight: case TMPro.TextAlignmentOptions.BottomRight:
case TextAnchor.MiddleRight: case TMPro.TextAlignmentOptions.MidlineRight:
case TextAnchor.UpperRight: case TMPro.TextAlignmentOptions.TopRight:
alignmentFactor = 1f; alignmentFactor = 1f;
break; break;
} }
#else
for (int lineIdx=0; lineIdx < lines.Length; lineIdx++) switch (text.alignment)
{
case TextAnchor.LowerLeft:
case TextAnchor.MiddleLeft:
case TextAnchor.UpperLeft:
alignmentFactor = 0f;
break;
case TextAnchor.LowerCenter:
case TextAnchor.MiddleCenter:
case TextAnchor.UpperCenter:
alignmentFactor = 0.5f;
break;
case TextAnchor.LowerRight:
case TextAnchor.MiddleRight:
case TextAnchor.UpperRight:
alignmentFactor = 1f;
break;
}
#endif
for (int lineIdx=0; lineIdx < lines.Length; lineIdx++)
{ {
string line = lines[lineIdx]; string line = lines[lineIdx];
float lineOffset = (line.Length - 1) * letterOffset * (alignmentFactor) - (alignmentFactor - 0.5f) * rectTransform.rect.width; float lineOffset = (line.Length - 1) * letterOffset * (alignmentFactor) - (alignmentFactor - 0.5f) * rectTransform.rect.width;

View File

@ -1,7 +1,9 @@
/// Credit Melang, Lee Hui /// Credit Melang, Lee Hui
/// Sourced from - http://forum.unity3d.com/members/melang.593409/ /// Sourced from - http://forum.unity3d.com/members/melang.593409/
/// GC Alloc fix - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/pull-requests/130 /// GC Alloc fix - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/pull-requests/130
/// NOT supported in Unity 2022
#if !UNITY_2022_1_OR_NEWER
using System.Collections.Generic; using System.Collections.Generic;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
@ -192,4 +194,5 @@ namespace UnityEngine.UI.Extensions
} }
#endif #endif
} }
} }
#endif

View File

@ -96,10 +96,8 @@ namespace UnityEngine.UI.Extensions
{ {
effectRoot.SetActive(true); effectRoot.SetActive(true);
} }
{
}
} }
void OnDestroy() void OnDestroy()
{ {
if (!Application.isPlaying) if (!Application.isPlaying)

View File

@ -1,4 +1,6 @@
/// Credit NemoKrad (aka Charles Humphrey) / valtain 
using static System.Net.Mime.MediaTypeNames;
/// Credit NemoKrad (aka Charles Humphrey) / valtain
/// Sourced from - http://www.randomchaos.co.uk/SoftAlphaUIMask.aspx /// Sourced from - http://www.randomchaos.co.uk/SoftAlphaUIMask.aspx
/// Updated by valtain - https://bitbucket.org/SimonDarksideJ/unity-ui-extensions/pull-requests/33 /// Updated by valtain - https://bitbucket.org/SimonDarksideJ/unity-ui-extensions/pull-requests/33
@ -45,7 +47,11 @@ namespace UnityEngine.UI.Extensions
MaskArea = GetComponent<RectTransform>(); MaskArea = GetComponent<RectTransform>();
} }
#if UNITY_2022_1_OR_NEWER
var text = GetComponent<TMPro.TMP_Text>();
#else
var text = GetComponent<Text>(); var text = GetComponent<Text>();
#endif
if (text != null) if (text != null)
{ {
mat = new Material(ShaderLibrary.GetShaderInstance("UI Extensions/SoftMaskShader")); mat = new Material(ShaderLibrary.GetShaderInstance("UI Extensions/SoftMaskShader"));

View File

@ -398,6 +398,7 @@ namespace UnityEngine.UI.Extensions
{ {
currentMaterial = null; currentMaterial = null;
currentTexture = null; currentTexture = null;
base.OnDestroy();
} }
public void StartParticleEmission() public void StartParticleEmission()

View File

@ -29,6 +29,13 @@ namespace UnityEngine.UI.Extensions
public float centerpoint = 0.5f; public float centerpoint = 0.5f;
protected override void OnEnable() { base.OnEnable(); CalculateRadial(); } protected override void OnEnable() { base.OnEnable(); CalculateRadial(); }
protected override void OnDisable()
{
m_Tracker.Clear();
LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
}
public override void SetLayoutHorizontal() { public override void SetLayoutHorizontal() {
} }
public override void SetLayoutVertical() { public override void SetLayoutVertical() {

View File

@ -1,7 +1,6 @@
/// Credit setchi (https://github.com/setchi) /// Credit setchi (https://github.com/setchi)
/// Sourced from - https://github.com/setchi/FancyScrollView /// Sourced from - https://github.com/setchi/FancyScrollView
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
/// <summary> /// <summary>

View File

@ -101,7 +101,7 @@ namespace UnityEngine.UI.Extensions
/// <param name="p"><see cref="Scroller"/> のスクロール位置.</param> /// <param name="p"><see cref="Scroller"/> のスクロール位置.</param>
void OnScrollerValueChanged(float p) void OnScrollerValueChanged(float p)
{ {
base.UpdatePosition(Scrollable ? ToFancyScrollViewPosition(p) : 0f); base.UpdatePosition(ToFancyScrollViewPosition(Scrollable ? p : 0f));
if (Scroller.Scrollbar) if (Scroller.Scrollbar)
{ {
@ -161,8 +161,6 @@ namespace UnityEngine.UI.Extensions
/// <inheritdoc/> /// <inheritdoc/>
protected override void UpdateContents(IList<TItemData> items) protected override void UpdateContents(IList<TItemData> items)
{ {
Debug.Assert(Context.CalculateScrollSize != null);
AdjustCellIntervalAndScrollOffset(); AdjustCellIntervalAndScrollOffset();
base.UpdateContents(items); base.UpdateContents(items);

View File

@ -53,4 +53,4 @@ namespace UnityEngine.UI.Extensions
/// <inheritdoc/> /// <inheritdoc/>
public sealed override void SetContext(FancyScrollRectContext context) => base.SetContext(context); public sealed override void SetContext(FancyScrollRectContext context) => base.SetContext(context);
} }
} }

View File

@ -329,7 +329,7 @@ namespace UnityEngine.UI.Extensions
if (hold && snap.Enable) if (hold && snap.Enable)
{ {
UpdateSelection(Mathf.Clamp(Mathf.RoundToInt(currentPosition), 0, totalCount - 1)); UpdateSelection(Mathf.RoundToInt(CircularPosition(currentPosition, totalCount)));
ScrollTo(Mathf.RoundToInt(currentPosition), snap.Duration, snap.Easing); ScrollTo(Mathf.RoundToInt(currentPosition), snap.Duration, snap.Easing);
} }

View File

@ -166,7 +166,6 @@ namespace UnityEngine.UI.Extensions
childSize = LayoutUtility.GetPreferredSize (child, 0); childSize = LayoutUtility.GetPreferredSize (child, 0);
childSize = Mathf.Min (childSize, workingSize); childSize = Mathf.Min (childSize, workingSize);
childOtherSize = LayoutUtility.GetPreferredSize (child, 1); childOtherSize = LayoutUtility.GetPreferredSize (child, 1);
childOtherSize = Mathf.Min (childOtherSize, workingSize);
} else if (startAxis == Axis.Vertical) { } else if (startAxis == Axis.Vertical) {
if (invertOrder) { if (invertOrder) {
index = IsRightAlign ? rectChildren.Count - 1 - i : i; index = IsRightAlign ? rectChildren.Count - 1 - i : i;
@ -175,7 +174,6 @@ namespace UnityEngine.UI.Extensions
childSize = LayoutUtility.GetPreferredSize (child, 1); childSize = LayoutUtility.GetPreferredSize (child, 1);
childSize = Mathf.Min (childSize, workingSize); childSize = Mathf.Min (childSize, workingSize);
childOtherSize = LayoutUtility.GetPreferredSize (child, 0); childOtherSize = LayoutUtility.GetPreferredSize (child, 0);
childOtherSize = Mathf.Min (childOtherSize, workingSize);
} }
// If adding this element would exceed the bounds of the container, // If adding this element would exceed the bounds of the container,
@ -227,11 +225,11 @@ namespace UnityEngine.UI.Extensions
if (startAxis == Axis.Horizontal) { if (startAxis == Axis.Horizontal) {
float newOffset = CalculateRowVerticalOffset (groupHeight, offset, currentBarSpace); float newOffset = CalculateRowVerticalOffset (groupHeight, offset, currentBarSpace);
currentBarSize -= spacingBetweenElements; currentBarSize -= spacingBetweenElements;
LayoutRow (_itemList, currentBarSize, currentBarSpace, workingSize - (ChildForceExpandWidth ? 0 : spacingBetweenElements), padding.left, newOffset, axis); LayoutRow (_itemList, currentBarSize, currentBarSpace, workingSize, padding.left, newOffset, axis);
}else if (startAxis == Axis.Vertical) { }else if (startAxis == Axis.Vertical) {
float newOffset = CalculateColHorizontalOffset(groupWidth, offset, currentBarSpace); float newOffset = CalculateColHorizontalOffset(groupWidth, offset, currentBarSpace);
currentBarSize -= spacingBetweenElements; currentBarSize -= spacingBetweenElements;
LayoutCol(_itemList, currentBarSpace, currentBarSize, workingSize - (ChildForceExpandHeight ? 0 : spacingBetweenElements), newOffset, padding.top, axis); LayoutCol(_itemList, currentBarSpace, currentBarSize, workingSize, newOffset, padding.top, axis);
} }
} }
@ -424,5 +422,13 @@ namespace UnityEngine.UI.Extensions
} }
return max; return max;
} }
}
protected override void OnDisable()
{
m_Tracker.Clear();
LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
}
}
} }

View File

@ -114,12 +114,20 @@ namespace UnityEngine.UI.Extensions
/// <param name="WorldPositionStays">Should the world position be updated to it's parent transform?</param> /// <param name="WorldPositionStays">Should the world position be updated to it's parent transform?</param>
public void AddChild(GameObject GO, bool WorldPositionStays) public void AddChild(GameObject GO, bool WorldPositionStays)
{ {
_scroll_rect.horizontalNormalizedPosition = 0; try
{
// Rare instances of Unity bug cause error, adding try to manage it.
_scroll_rect.horizontalNormalizedPosition = 0;
}
catch { }
GO.transform.SetParent(_screensContainer, WorldPositionStays); GO.transform.SetParent(_screensContainer, WorldPositionStays);
InitialiseChildObjectsFromScene(); InitialiseChildObjectsFromScene();
DistributePages(); DistributePages();
if (MaskArea) if (MaskArea)
{
UpdateVisible(); UpdateVisible();
}
SetScrollContainerPosition(); SetScrollContainerPosition();
} }
@ -149,7 +157,12 @@ namespace UnityEngine.UI.Extensions
{ {
return; return;
} }
_scroll_rect.horizontalNormalizedPosition = 0; try
{
// Rare instances of Unity bug cause error, adding try to manage it.
_scroll_rect.horizontalNormalizedPosition = 0;
}
catch { }
Transform child = _screensContainer.transform.GetChild(index); Transform child = _screensContainer.transform.GetChild(index);
child.SetParent(null, WorldPositionStays); child.SetParent(null, WorldPositionStays);

View File

@ -55,6 +55,13 @@ namespace UnityEngine.UI.Extensions
CalculateRadial(); CalculateRadial();
} }
#endif #endif
protected override void OnDisable()
{
m_Tracker.Clear(); // key change - do not restore - false
LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
}
void CalculateRadial() void CalculateRadial()
{ {
m_Tracker.Clear(); m_Tracker.Clear();

View File

@ -2,11 +2,11 @@
/// Sourced from - https://github.com/setchi/FancyScrollView /// Sourced from - https://github.com/setchi/FancyScrollView
using System; using System;
using UnityEngine.Events;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[Obsolete("ScrollPositionController has been replaced by the Scroller component", true)]
public class ScrollPositionController : UIBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler public class ScrollPositionController : UIBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler
{ {
[SerializeField] [SerializeField]

View File

@ -5,7 +5,6 @@
using System; using System;
using UnityEngine.Events; using UnityEngine.Events;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityEngine.UI.Extensions;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
@ -14,9 +13,7 @@ namespace UnityEngine.UI.Extensions
internal Rect panelDimensions; internal Rect panelDimensions;
internal RectTransform _screensContainer; internal RectTransform _screensContainer;
internal bool _isVertical; internal bool _isVertical;
internal int _screens = 1; internal int _screens = 1;
internal float _scrollStartPosition; internal float _scrollStartPosition;
internal float _childSize; internal float _childSize;
private float _childPos, _maskSize; private float _childPos, _maskSize;
@ -27,7 +24,6 @@ namespace UnityEngine.UI.Extensions
internal bool _pointerDown = false; internal bool _pointerDown = false;
internal bool _settled = true; internal bool _settled = true;
internal Vector3 _startPosition = new Vector3(); internal Vector3 _startPosition = new Vector3();
[Tooltip("The currently active page")]
internal int _currentPage; internal int _currentPage;
internal int _previousPage; internal int _previousPage;
internal int _halfNoVisibleItems; internal int _halfNoVisibleItems;
@ -37,7 +33,7 @@ namespace UnityEngine.UI.Extensions
private int _bottomItem, _topItem; private int _bottomItem, _topItem;
internal bool _startEventCalled = false; internal bool _startEventCalled = false;
internal bool _endEventCalled = false; internal bool _endEventCalled = false;
internal bool _suspendEvents = false; internal bool _suspendEvents = false;
[Serializable] [Serializable]
public class SelectionChangeStartEvent : UnityEvent { } public class SelectionChangeStartEvent : UnityEvent { }
@ -68,25 +64,25 @@ namespace UnityEngine.UI.Extensions
public float transitionSpeed = 7.5f; public float transitionSpeed = 7.5f;
[Tooltip("Hard Swipe forces to swiping to the next / previous page (optional)")] [Tooltip("Hard Swipe forces to swiping to the next / previous page (optional)")]
public Boolean UseHardSwipe = false; public bool UseHardSwipe = false;
[Tooltip("Fast Swipe makes swiping page next / previous (optional)")] [Tooltip("Fast Swipe makes swiping page next / previous (optional)")]
public Boolean UseFastSwipe = false; public bool UseFastSwipe = false;
[Tooltip("Swipe Delta Threshold looks at the speed of input to decide if a swipe will be initiated (optional)")] [Tooltip("Swipe Delta Threshold looks at the speed of input to decide if a swipe will be initiated (optional)")]
public Boolean UseSwipeDeltaThreshold = false; public bool UseSwipeDeltaThreshold = false;
[Tooltip("Offset for how far a swipe has to travel to initiate a page change (optional)")] [Tooltip("Offset for how far a swipe has to travel to initiate a page change (optional)")]
public int FastSwipeThreshold = 100; public int FastSwipeThreshold = 100;
[Tooltip("Speed at which the ScrollRect will keep scrolling before slowing down and stopping (optional)")] [Tooltip("Speed at which the ScrollRect will keep scrolling before slowing down and stopping (optional)")]
public int SwipeVelocityThreshold = 100; public int SwipeVelocityThreshold = 100;
[Tooltip("Threshold for swipe speed to initiate a swipe, below threshold will return to closest page (optional)")] [Tooltip("Threshold for swipe speed to initiate a swipe, below threshold will return to closest page (optional)")]
public float SwipeDeltaThreshold = 5.0f; public float SwipeDeltaThreshold = 5.0f;
[Tooltip("Use time scale instead of unscaled time (optional)")] [Tooltip("Use time scale instead of unscaled time (optional)")]
public Boolean UseTimeScale = true; public bool UseTimeScale = true;
[Tooltip("The visible bounds area, controls which items are visible/enabled. *Note Should use a RectMask. (optional)")] [Tooltip("The visible bounds area, controls which items are visible/enabled. *Note Should use a RectMask. (optional)")]
public RectTransform MaskArea; public RectTransform MaskArea;
@ -168,7 +164,6 @@ namespace UnityEngine.UI.Extensions
private SelectionChangeEndEvent m_OnSelectionChangeEndEvent = new SelectionChangeEndEvent(); private SelectionChangeEndEvent m_OnSelectionChangeEndEvent = new SelectionChangeEndEvent();
public SelectionChangeEndEvent OnSelectionChangeEndEvent { get { return m_OnSelectionChangeEndEvent; } set { m_OnSelectionChangeEndEvent = value; } } public SelectionChangeEndEvent OnSelectionChangeEndEvent { get { return m_OnSelectionChangeEndEvent; } set { m_OnSelectionChangeEndEvent = value; } }
// Use this for initialization
void Awake() void Awake()
{ {
if (_scroll_rect == null) if (_scroll_rect == null)
@ -186,7 +181,7 @@ namespace UnityEngine.UI.Extensions
vscroll.ss = this; vscroll.ss = this;
} }
panelDimensions = gameObject.GetComponent<RectTransform>().rect; panelDimensions = gameObject.GetComponent<RectTransform>().rect;
if (StartingScreen < 0) if (StartingScreen < 0)
{ {
StartingScreen = 0; StartingScreen = 0;
@ -315,8 +310,6 @@ namespace UnityEngine.UI.Extensions
//Function for switching screens with buttons //Function for switching screens with buttons
public void NextScreen() public void NextScreen()
{ {
_scroll_rect.velocity = Vector2.zero;
if (_currentPage < _screens - 1 || _isInfinite) if (_currentPage < _screens - 1 || _isInfinite)
{ {
if (!_lerp) StartScreenChange(); if (!_lerp) StartScreenChange();
@ -339,8 +332,6 @@ namespace UnityEngine.UI.Extensions
//Function for switching screens with buttons //Function for switching screens with buttons
public void PreviousScreen() public void PreviousScreen()
{ {
_scroll_rect.velocity = Vector2.zero;
if (_currentPage > 0 || _isInfinite) if (_currentPage > 0 || _isInfinite)
{ {
if (!_lerp) StartScreenChange(); if (!_lerp) StartScreenChange();
@ -419,7 +410,7 @@ namespace UnityEngine.UI.Extensions
else else
{ {
_infiniteOffset = _screensContainer.anchoredPosition.x < 0 ? -_screensContainer.sizeDelta.x * _infiniteWindow : _screensContainer.sizeDelta.x * _infiniteWindow; _infiniteOffset = _screensContainer.anchoredPosition.x < 0 ? -_screensContainer.sizeDelta.x * _infiniteWindow : _screensContainer.sizeDelta.x * _infiniteWindow;
_infiniteOffset = _infiniteOffset == 0 ? 0 : _infiniteOffset < 0 ? _infiniteOffset - _childSize * _infiniteWindow : _infiniteOffset + _childSize * _infiniteWindow; _infiniteOffset = _infiniteOffset == 0 ? 0 : _infiniteOffset < 0 ? _infiniteOffset - _childSize * _infiniteWindow : _infiniteOffset + _childSize * _infiniteWindow;
target.x = _childPos + _scrollStartPosition + _infiniteOffset; target.x = _childPos + _scrollStartPosition + _infiniteOffset;
} }
} }
@ -520,8 +511,15 @@ namespace UnityEngine.UI.Extensions
MaskBuffer = 1; MaskBuffer = 1;
} }
PageStep.Clamp(0, 9); if (PageStep < 0)
{
PageStep = 0;
}
if (PageStep > 8)
{
PageStep = 9;
}
var infiniteScroll = GetComponent<UI_InfiniteScroll>(); var infiniteScroll = GetComponent<UI_InfiniteScroll>();
if (ChildObjects != null && ChildObjects.Length > 0 && infiniteScroll != null && !infiniteScroll.InitByUser) if (ChildObjects != null && ChildObjects.Length > 0 && infiniteScroll != null && !infiniteScroll.InitByUser)
{ {
@ -645,4 +643,4 @@ namespace UnityEngine.UI.Extensions
#endregion #endregion
} }
} }

View File

@ -284,5 +284,11 @@ namespace UnityEngine.UI.Extensions
// Set preferredRowHeights to null to free memory // Set preferredRowHeights to null to free memory
preferredRowHeights = null; preferredRowHeights = null;
} }
protected override void OnDisable()
{
m_Tracker.Clear(); // key change - do not restore - false
LayoutRebuilder.MarkLayoutForRebuild(rectTransform);
}
} }
} }

View File

@ -1,10 +1,12 @@
/// Credit Ges /// Credit Ges
/// Sourced from - http://forum.unity3d.com/threads/scripts-useful-4-6-scripts-collection.264161/page-3#post-2280109 /// Sourced from - http://forum.unity3d.com/threads/scripts-useful-4-6-scripts-collection.264161/page-3#post-2280109
using System;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
[Obsolete("TileSizeFitter will be deprecated in next version as Unity has disabled this feature")]
[ExecuteInEditMode] [ExecuteInEditMode]
[RequireComponent(typeof(RectTransform))] [RequireComponent(typeof(RectTransform))]
[AddComponentMenu("Layout/Extensions/Tile Size Fitter")] [AddComponentMenu("Layout/Extensions/Tile Size Fitter")]

View File

@ -0,0 +1,276 @@
/// Credit Ahmad S. Al-Faqeeh
/// Sourced from - https://github.com/Unity-UI-Extensions/com.unity.uiextensions/issues/205
/// Based on the UIVerticalScroller
///
using UnityEngine.Events;
namespace UnityEngine.UI.Extensions
{
[RequireComponent(typeof(ScrollRect))]
[AddComponentMenu("Layout/Extensions/Horizontal Scroller")]
public class UIHorizontalScroller : MonoBehaviour
{
private float[] distReposition;
private float[] distance;
[SerializeField]
[Tooltip("desired ScrollRect")]
private ScrollRect scrollRect;
[SerializeField]
[Tooltip("Elements to populate inside the scroller")]
private GameObject[] arrayOfElements;
[SerializeField]
[Tooltip("Center display area (position of zoomed content)")]
private RectTransform center;
[SerializeField]
[Tooltip("Size / spacing of elements")]
private RectTransform elementSize;
[SerializeField]
[Tooltip("Scale = 1/ (1+distance from center * shrinkage)")]
private Vector2 elementShrinkage = new Vector2(1f / 200, 1f / 200);
[SerializeField]
[Tooltip("Minimum element scale (furthest from center)")]
private Vector2 minScale = new Vector2(0.7f, 0.7f);
[SerializeField]
[Tooltip("Select the item to be in center on start. (optional)")]
private int startingIndex = -1;
[SerializeField]
[Tooltip("Stop scrolling past last element from inertia.")]
private bool stopMomentumOnEnd = true;
[SerializeField]
[Tooltip("Set Items out of center to not interactible.")]
private bool disableUnfocused = true;
[SerializeField]
[Tooltip("Button to go to the next page. (optional)")]
private GameObject scrollLeftButton;
[SerializeField]
[Tooltip("Button to go to the previous page. (optional)")]
private GameObject scrollRightButton;
[SerializeField]
[Tooltip("Event fired when a specific item is clicked, exposes index number of item. (optional)")]
private UnityEvent<int> onButtonClicked;
[SerializeField]
[Tooltip("Event fired when the focused item is Changed. (optional)")]
private UnityEvent<int> onFocusChanged;
public int FocusedElementIndex { get; private set; }
public RectTransform Center { get => center; set => center = value; }
//Scrollable area (content of desired ScrollRect)
public RectTransform ScrollingPanel { get { return scrollRect.content; } }
public string Result { get; private set; }
public UIHorizontalScroller() { }
public UIHorizontalScroller(RectTransform center, RectTransform elementSize, ScrollRect scrollRect, GameObject[] arrayOfElements)
{
this.scrollRect = scrollRect;
this.elementSize = elementSize;
this.arrayOfElements = arrayOfElements;
this.center = center;
}
public void Awake()
{
if (!scrollRect)
{
scrollRect = GetComponent<ScrollRect>();
}
if (!center)
{
Debug.LogError("Please define the RectTransform for the Center viewport of the scrollable area");
}
if (!elementSize)
{
elementSize = center;
}
if (arrayOfElements == null || arrayOfElements.Length == 0)
{
var childCount = scrollRect.content.childCount;
if (childCount > 0)
{
arrayOfElements = new GameObject[childCount];
for (int i = 0; i < childCount; i++)
{
arrayOfElements[i] = scrollRect.content.GetChild(i).gameObject;
}
}
}
}
public void Start()
{
if (scrollLeftButton)
{
scrollLeftButton.GetComponent<Button>().onClick.AddListener(() => ScrollLeft());
}
if (scrollRightButton)
{
scrollRightButton.GetComponent<Button>().onClick.AddListener(() => ScrollRight());
}
UpdateChildren(startingIndex, arrayOfElements);
}
/// <summary>
/// Recognises and resizes the children.
/// </summary>
/// <param name="startingIndex">Starting index.</param>
/// <param name="arrayOfElements">Array of elements.</param>
public void UpdateChildren(int startingIndex = -1, GameObject[] arrayOfElements = null)
{
// Set _arrayOfElements to arrayOfElements if given, otherwise to child objects of the scrolling panel.
if (arrayOfElements != null)
{
this.arrayOfElements = arrayOfElements;
}
else
{
this.arrayOfElements = new GameObject[ScrollingPanel.childCount];
for (int i = 0; i < ScrollingPanel.childCount; i++)
{
this.arrayOfElements[i] = ScrollingPanel.GetChild(i).gameObject;
}
}
// resize the elements to match elementSize rect
for (var i = 0; i < this.arrayOfElements.Length; i++)
{
AddListener(arrayOfElements[i], i);
RectTransform r = this.arrayOfElements[i].GetComponent<RectTransform>();
r.anchorMax = r.anchorMin = r.pivot = new Vector2(0.5f, 0.5f);
r.localPosition = new Vector2(i * elementSize.rect.size.x,0);
r.sizeDelta = elementSize.rect.size;
}
// prepare for scrolling
distance = new float[this.arrayOfElements.Length];
distReposition = new float[this.arrayOfElements.Length];
FocusedElementIndex = -1;
// if starting index is given, snap to respective element
if (startingIndex > -1)
{
startingIndex = startingIndex > this.arrayOfElements.Length ? this.arrayOfElements.Length - 1 : startingIndex;
SnapToElement(startingIndex);
}
}
private void AddListener(GameObject button, int index)
{
var buttonClick = button.GetComponent<Button>();
buttonClick.onClick.RemoveAllListeners();
buttonClick.onClick.AddListener(() => onButtonClicked?.Invoke(index));
}
public void Update()
{
if (arrayOfElements.Length < 1)
{
return;
}
for (var i = 0; i < arrayOfElements.Length; i++)
{
var arrayElementRT = arrayOfElements[i].GetComponent<RectTransform>();
distReposition[i] = center.position.x - arrayElementRT.position.x;
distance[i] = Mathf.Abs(distReposition[i]);
//Magnifying effect
Vector2 scale = Vector2.Max(minScale, new Vector2(1 / (1 + distance[i] * elementShrinkage.x), (1 / (1 + distance[i] * elementShrinkage.y))));
arrayElementRT.transform.localScale = new Vector3(scale.x, scale.y, 1f);
}
float minDistance = Mathf.Min(distance);
int oldFocusedElement = FocusedElementIndex;
for (var i = 0; i < arrayOfElements.Length; i++)
{
arrayOfElements[i].GetComponent<CanvasGroup>().interactable = !disableUnfocused || minDistance == distance[i];
if (minDistance == distance[i])
{
FocusedElementIndex = i;
#if UNITY_2022_1_OR_NEWER
var textComponentTxtMeshPro = arrayOfElements[i].GetComponentInChildren<TMPro.TMP_Text>();
if (textComponentTxtMeshPro != null)
{
Result = textComponentTxtMeshPro.text;
}
#else
var textComponent = arrayOfElements[i].GetComponentInChildren<Text>();
if (textComponent != null)
{
Result = textComponent.text;
}
#endif
}
}
if (FocusedElementIndex != oldFocusedElement && onFocusChanged != null)
{
onFocusChanged.Invoke(FocusedElementIndex);
}
if (!UIExtensionsInputManager.GetMouseButton(0))
{
// scroll slowly to nearest element when not dragged
ScrollingElements();
}
// stop scrolling past last element from inertia
if (stopMomentumOnEnd
&& (arrayOfElements[0].GetComponent<RectTransform>().position.x > center.position.x
|| arrayOfElements[arrayOfElements.Length - 1].GetComponent<RectTransform>().position.x < center.position.x))
{
scrollRect.velocity = Vector2.zero;
}
}
private void ScrollingElements()
{
float newX = Mathf.Lerp(ScrollingPanel.anchoredPosition.x, ScrollingPanel.anchoredPosition.x + distReposition[FocusedElementIndex], Time.deltaTime * 2f);
Vector2 newPosition = new Vector2(newX, ScrollingPanel.anchoredPosition.y);
ScrollingPanel.anchoredPosition = newPosition;
}
public void SnapToElement(int element)
{
float deltaElementPositionX = elementSize.rect.width / 1.2f * element;
Vector2 newPosition = new Vector2(-deltaElementPositionX, ScrollingPanel.anchoredPosition.y);
ScrollingPanel.anchoredPosition = newPosition;
}
public void ScrollLeft()
{
float deltaLeft = elementSize.rect.width / 1.2f;
Vector2 newPositionLeft = new Vector2(ScrollingPanel.anchoredPosition.x - deltaLeft, ScrollingPanel.anchoredPosition.y);
ScrollingPanel.anchoredPosition = Vector2.Lerp(ScrollingPanel.anchoredPosition, newPositionLeft, 1);
}
public void ScrollRight()
{
float deltaRight = elementSize.rect.width / 1.2f;// arrayOfElements[0].GetComponent<RectTransform>().rect.width;
Vector2 newPositionRight = new Vector2(ScrollingPanel.anchoredPosition.x + deltaRight, ScrollingPanel.anchoredPosition.y);
ScrollingPanel.anchoredPosition = newPositionRight;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8fe84cbf30cb0874091fd899fe1457d4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -10,53 +10,69 @@ namespace UnityEngine.UI.Extensions
[AddComponentMenu("Layout/Extensions/Vertical Scroller")] [AddComponentMenu("Layout/Extensions/Vertical Scroller")]
public class UIVerticalScroller : MonoBehaviour public class UIVerticalScroller : MonoBehaviour
{ {
[Tooltip("desired ScrollRect")]
public ScrollRect scrollRect;
[Tooltip("Center display area (position of zoomed content)")]
public RectTransform center;
[Tooltip("Size / spacing of elements")]
public RectTransform elementSize;
[Tooltip("Scale = 1/ (1+distance from center * shrinkage)")]
public Vector2 elementShrinkage = new Vector2(1f / 200, 1f / 200);
[Tooltip("Minimum element scale (furthest from center)")]
public Vector2 minScale = new Vector2(0.7f, 0.7f);
[Tooltip("Select the item to be in center on start.")]
public int startingIndex = -1;
[Tooltip("Stop scrolling past last element from inertia.")]
public bool stopMomentumOnEnd = true;
[Tooltip("Set Items out of center to not interactible.")]
public bool disableUnfocused = true;
[Tooltip("Button to go to the next page. (optional)")]
public GameObject scrollUpButton;
[Tooltip("Button to go to the previous page. (optional)")]
public GameObject scrollDownButton;
[Tooltip("Event fired when a specific item is clicked, exposes index number of item. (optional)")]
public IntEvent OnButtonClicked;
[Tooltip("Event fired when the focused item is Changed. (optional)")]
public IntEvent OnFocusChanged;
[HideInInspector]
public GameObject[] _arrayOfElements;
public int focusedElementIndex { get; private set; }
public string result { get; private set; }
private float[] distReposition; private float[] distReposition;
private float[] distance; private float[] distance;
//private int elementsDistance;
[SerializeField]
[Tooltip("desired ScrollRect")]
private ScrollRect scrollRect;
[SerializeField]
[Tooltip("Elements to populate inside the scroller")]
private GameObject[] arrayOfElements;
[SerializeField]
[Tooltip("Center display area (position of zoomed content)")]
private RectTransform center;
[SerializeField]
[Tooltip("Size / spacing of elements")]
private RectTransform elementSize;
[SerializeField]
[Tooltip("Scale = 1/ (1+distance from center * shrinkage)")]
private Vector2 elementShrinkage = new Vector2(1f / 200, 1f / 200);
[SerializeField]
[Tooltip("Minimum element scale (furthest from center)")]
private Vector2 minScale = new Vector2(0.7f, 0.7f);
[SerializeField]
[Tooltip("Select the item to be in center on start.")]
private int startingIndex = -1;
[SerializeField]
[Tooltip("Stop scrolling past last element from inertia.")]
private bool stopMomentumOnEnd = true;
[SerializeField]
[Tooltip("Set Items out of center to not interactible.")]
private bool disableUnfocused = true;
[SerializeField]
[Tooltip("Button to go to the next page. (optional)")]
private GameObject scrollUpButton;
[SerializeField]
[Tooltip("Button to go to the previous page. (optional)")]
private GameObject scrollDownButton;
[SerializeField]
[Tooltip("Event fired when a specific item is clicked, exposes index number of item. (optional)")]
private UnityEvent<int> onButtonClicked;
[SerializeField]
[Tooltip("Event fired when the focused item is Changed. (optional)")]
private UnityEvent<int> onFocusChanged;
public int FocusedElementIndex { get; private set; }
public RectTransform Center { get => center; set => center = value; }
public string Result { get; private set; }
//Scrollable area (content of desired ScrollRect) //Scrollable area (content of desired ScrollRect)
[HideInInspector] public RectTransform ScrollingPanel{ get { return scrollRect.content; } }
public RectTransform scrollingPanel{ get { return scrollRect.content; } }
/// <summary>
/// Constructor when not used as component but called from other script, don't forget to set the non-optional properties.
/// </summary>
public UIVerticalScroller()
{
}
/// <summary> /// <summary>
/// Constructor when not used as component but called from other script /// Constructor when not used as component but called from other script
@ -66,7 +82,7 @@ namespace UnityEngine.UI.Extensions
this.center = center; this.center = center;
this.elementSize = elementSize; this.elementSize = elementSize;
this.scrollRect = scrollRect; this.scrollRect = scrollRect;
_arrayOfElements = arrayOfElements; this.arrayOfElements = arrayOfElements;
} }
/// <summary> /// <summary>
@ -78,21 +94,28 @@ namespace UnityEngine.UI.Extensions
{ {
scrollRect = GetComponent<ScrollRect>(); scrollRect = GetComponent<ScrollRect>();
} }
if (!center) if (!center)
{ {
Debug.LogError("Please define the RectTransform for the Center viewport of the scrollable area"); Debug.LogError("Please define the RectTransform for the Center viewport of the scrollable area");
} }
if (!elementSize) if (!elementSize)
{ {
elementSize = center; elementSize = center;
} }
if (_arrayOfElements == null || _arrayOfElements.Length == 0)
if (arrayOfElements == null || arrayOfElements.Length == 0)
{ {
_arrayOfElements = new GameObject[scrollingPanel.childCount]; var childCount = ScrollingPanel.childCount;
for (int i = 0; i < scrollingPanel.childCount; i++) if (childCount > 0)
{ {
_arrayOfElements[i] = scrollingPanel.GetChild(i).gameObject; arrayOfElements = new GameObject[childCount];
} for (int i = 0; i < childCount; i++)
{
arrayOfElements[i] = ScrollingPanel.GetChild(i).gameObject;
}
}
} }
} }
@ -101,104 +124,114 @@ namespace UnityEngine.UI.Extensions
/// </summary> /// </summary>
/// <param name="startingIndex">Starting index.</param> /// <param name="startingIndex">Starting index.</param>
/// <param name="arrayOfElements">Array of elements.</param> /// <param name="arrayOfElements">Array of elements.</param>
public void updateChildren(int startingIndex = -1, GameObject[] arrayOfElements = null) public void UpdateChildren(int startingIndex = -1, GameObject[] arrayOfElements = null)
{ {
// Set _arrayOfElements to arrayOfElements if given, otherwise to child objects of the scrolling panel. // Set _arrayOfElements to arrayOfElements if given, otherwise to child objects of the scrolling panel.
if (arrayOfElements != null) if (arrayOfElements != null)
{ {
_arrayOfElements = arrayOfElements; this.arrayOfElements = arrayOfElements;
} }
else else
{ {
_arrayOfElements = new GameObject[scrollingPanel.childCount]; this.arrayOfElements = new GameObject[ScrollingPanel.childCount];
for (int i = 0; i < scrollingPanel.childCount; i++) for (int i = 0; i < ScrollingPanel.childCount; i++)
{ {
_arrayOfElements[i] = scrollingPanel.GetChild(i).gameObject; this.arrayOfElements[i] = ScrollingPanel.GetChild(i).gameObject;
} }
} }
// resize the elements to match elementSize rect // resize the elements to match elementSize rect
for (var i = 0; i < _arrayOfElements.Length; i++) for (var i = 0; i < this.arrayOfElements.Length; i++)
{ {
int j = i; AddListener(arrayOfElements[i], i);
_arrayOfElements[i].GetComponent<Button>().onClick.RemoveAllListeners();
if (OnButtonClicked != null) RectTransform r = this.arrayOfElements[i].GetComponent<RectTransform>();
{
_arrayOfElements[i].GetComponent<Button>().onClick.AddListener(() => OnButtonClicked.Invoke(j));
}
RectTransform r = _arrayOfElements[i].GetComponent<RectTransform>();
r.anchorMax = r.anchorMin = r.pivot = new Vector2(0.5f, 0.5f); r.anchorMax = r.anchorMin = r.pivot = new Vector2(0.5f, 0.5f);
r.localPosition = new Vector2(0, i * elementSize.rect.size.y); r.localPosition = new Vector2(0, i * elementSize.rect.size.y);
r.sizeDelta = elementSize.rect.size; r.sizeDelta = elementSize.rect.size;
} }
// prepare for scrolling // prepare for scrolling
distance = new float[_arrayOfElements.Length]; distance = new float[this.arrayOfElements.Length];
distReposition = new float[_arrayOfElements.Length]; distReposition = new float[this.arrayOfElements.Length];
focusedElementIndex = -1; FocusedElementIndex = -1;
//scrollRect.scrollSensitivity = elementSize.rect.height / 5;
// if starting index is given, snap to respective element // if starting index is given, snap to respective element
if (startingIndex > -1) if (startingIndex > -1)
{ {
startingIndex = startingIndex > _arrayOfElements.Length ? _arrayOfElements.Length - 1 : startingIndex; startingIndex = startingIndex > this.arrayOfElements.Length ? this.arrayOfElements.Length - 1 : startingIndex;
SnapToElement(startingIndex); SnapToElement(startingIndex);
} }
} }
private void AddListener(GameObject button, int index)
{
var buttonClick = button.GetComponent<Button>();
buttonClick.onClick.RemoveAllListeners();
buttonClick.onClick.AddListener(() => onButtonClicked?.Invoke(index));
}
public void Start() public void Start()
{ {
if (scrollUpButton) if (scrollUpButton)
scrollUpButton.GetComponent<Button>().onClick.AddListener(() => {
{ scrollUpButton.GetComponent<Button>().onClick.AddListener(() => ScrollUp());
ScrollUp(); }
});
if (scrollDownButton) if (scrollDownButton)
scrollDownButton.GetComponent<Button>().onClick.AddListener(() => {
{ scrollDownButton.GetComponent<Button>().onClick.AddListener(() => ScrollDown());
ScrollDown(); }
}); UpdateChildren(startingIndex, arrayOfElements);
updateChildren(startingIndex, _arrayOfElements);
} }
public void Update() public void Update()
{ {
if (_arrayOfElements.Length < 1) if (arrayOfElements.Length < 1)
{ {
return; return;
} }
for (var i = 0; i < _arrayOfElements.Length; i++) for (var i = 0; i < arrayOfElements.Length; i++)
{ {
distReposition[i] = center.GetComponent<RectTransform>().position.y - _arrayOfElements[i].GetComponent<RectTransform>().position.y; var arrayElementRT = arrayOfElements[i].GetComponent<RectTransform>();
distReposition[i] = center.position.y - arrayElementRT.position.y;
distance[i] = Mathf.Abs(distReposition[i]); distance[i] = Mathf.Abs(distReposition[i]);
//Magnifying effect //Magnifying effect
Vector2 scale = Vector2.Max(minScale, new Vector2(1 / (1 + distance[i] * elementShrinkage.x), (1 / (1 + distance[i] * elementShrinkage.y)))); Vector2 scale = Vector2.Max(minScale, new Vector2(1 / (1 + distance[i] * elementShrinkage.x), (1 / (1 + distance[i] * elementShrinkage.y))));
_arrayOfElements[i].GetComponent<RectTransform>().transform.localScale = new Vector3(scale.x, scale.y, 1f); arrayElementRT.transform.localScale = new Vector3(scale.x, scale.y, 1f);
} }
// detect focused element // detect focused element
float minDistance = Mathf.Min(distance); float minDistance = Mathf.Min(distance);
int oldFocusedElement = focusedElementIndex; int oldFocusedElement = FocusedElementIndex;
for (var i = 0; i < _arrayOfElements.Length; i++)
for (var i = 0; i < arrayOfElements.Length; i++)
{ {
_arrayOfElements[i].GetComponent<CanvasGroup>().interactable = !disableUnfocused || minDistance == distance[i]; arrayOfElements[i].GetComponent<CanvasGroup>().interactable = !disableUnfocused || minDistance == distance[i];
if (minDistance == distance[i]) if (minDistance == distance[i])
{ {
focusedElementIndex = i; FocusedElementIndex = i;
result = _arrayOfElements[i].GetComponentInChildren<Text>().text; #if UNITY_2022_1_OR_NEWER
var textComponentTxtMeshPro = arrayOfElements[i].GetComponentInChildren<TMPro.TMP_Text>();
if (textComponentTxtMeshPro != null)
{
Result = textComponentTxtMeshPro.text;
}
#else
var textComponent = arrayOfElements[i].GetComponentInChildren<Text>();
if (textComponent != null)
{
Result = textComponent.text;
}
#endif
} }
} }
if (focusedElementIndex != oldFocusedElement && OnFocusChanged != null)
{
OnFocusChanged.Invoke(focusedElementIndex);
}
if (FocusedElementIndex != oldFocusedElement)
{
onFocusChanged?.Invoke(FocusedElementIndex);
}
if (!UIExtensionsInputManager.GetMouseButton(0)) if (!UIExtensionsInputManager.GetMouseButton(0))
{ {
@ -206,11 +239,10 @@ namespace UnityEngine.UI.Extensions
ScrollingElements(); ScrollingElements();
} }
// stop scrolling past last element from inertia // stop scrolling past last element from inertia
if (stopMomentumOnEnd if (stopMomentumOnEnd
&& (_arrayOfElements[0].GetComponent<RectTransform>().position.y > center.position.y && (arrayOfElements[0].GetComponent<RectTransform>().position.y > center.position.y
|| _arrayOfElements[_arrayOfElements.Length - 1].GetComponent<RectTransform>().position.y < center.position.y)) || arrayOfElements[arrayOfElements.Length - 1].GetComponent<RectTransform>().position.y < center.position.y))
{ {
scrollRect.velocity = Vector2.zero; scrollRect.velocity = Vector2.zero;
} }
@ -218,37 +250,30 @@ namespace UnityEngine.UI.Extensions
private void ScrollingElements() private void ScrollingElements()
{ {
float newY = Mathf.Lerp(scrollingPanel.anchoredPosition.y, scrollingPanel.anchoredPosition.y + distReposition[focusedElementIndex], Time.deltaTime * 2f); float newY = Mathf.Lerp(ScrollingPanel.anchoredPosition.y, ScrollingPanel.anchoredPosition.y + distReposition[FocusedElementIndex], Time.deltaTime * 2f);
Vector2 newPosition = new Vector2(scrollingPanel.anchoredPosition.x, newY); Vector2 newPosition = new Vector2(ScrollingPanel.anchoredPosition.x, newY);
scrollingPanel.anchoredPosition = newPosition; ScrollingPanel.anchoredPosition = newPosition;
} }
public void SnapToElement(int element) public void SnapToElement(int element)
{ {
float deltaElementPositionY = elementSize.rect.height * element; float deltaElementPositionY = elementSize.rect.height * element;
Vector2 newPosition = new Vector2(scrollingPanel.anchoredPosition.x, -deltaElementPositionY); Vector2 newPosition = new Vector2(ScrollingPanel.anchoredPosition.x, -deltaElementPositionY);
scrollingPanel.anchoredPosition = newPosition; ScrollingPanel.anchoredPosition = newPosition;
} }
public void ScrollUp() public void ScrollUp()
{ {
float deltaUp = elementSize.rect.height / 1.2f; float deltaUp = elementSize.rect.height / 1.2f;
Vector2 newPositionUp = new Vector2(scrollingPanel.anchoredPosition.x, scrollingPanel.anchoredPosition.y - deltaUp); Vector2 newPositionUp = new Vector2(ScrollingPanel.anchoredPosition.x, ScrollingPanel.anchoredPosition.y - deltaUp);
scrollingPanel.anchoredPosition = Vector2.Lerp(scrollingPanel.anchoredPosition, newPositionUp, 1); ScrollingPanel.anchoredPosition = Vector2.Lerp(ScrollingPanel.anchoredPosition, newPositionUp, 1);
} }
public void ScrollDown() public void ScrollDown()
{ {
float deltaDown = elementSize.rect.height / 1.2f; float deltaDown = elementSize.rect.height / 1.2f;
Vector2 newPositionDown = new Vector2(scrollingPanel.anchoredPosition.x, scrollingPanel.anchoredPosition.y + deltaDown); Vector2 newPositionDown = new Vector2(ScrollingPanel.anchoredPosition.x, ScrollingPanel.anchoredPosition.y + deltaDown);
scrollingPanel.anchoredPosition = newPositionDown; ScrollingPanel.anchoredPosition = newPositionDown;
}
[System.Serializable]
public class IntEvent:UnityEvent<int>
{
} }
} }
} }

View File

@ -114,7 +114,13 @@ namespace UnityEngine.UI.Extensions
/// <param name="WorldPositionStays">Should the world position be updated to it's parent transform?</param> /// <param name="WorldPositionStays">Should the world position be updated to it's parent transform?</param>
public void AddChild(GameObject GO, bool WorldPositionStays) public void AddChild(GameObject GO, bool WorldPositionStays)
{ {
_scroll_rect.verticalNormalizedPosition = 0; try
{
// Rare instances of Unity bug cause error, adding try to manage it.
_scroll_rect.verticalNormalizedPosition = 0;
}
catch { }
GO.transform.SetParent(_screensContainer, WorldPositionStays); GO.transform.SetParent(_screensContainer, WorldPositionStays);
InitialiseChildObjectsFromScene(); InitialiseChildObjectsFromScene();
DistributePages(); DistributePages();
@ -148,7 +154,12 @@ namespace UnityEngine.UI.Extensions
{ {
return; return;
} }
_scroll_rect.verticalNormalizedPosition = 0; try
{
// Rare instances of Unity bug cause error, adding try to manage it.
_scroll_rect.verticalNormalizedPosition = 0;
}
catch { }
Transform child = _screensContainer.transform.GetChild(index); Transform child = _screensContainer.transform.GetChild(index);
child.SetParent(null, WorldPositionStays); child.SetParent(null, WorldPositionStays);

View File

@ -155,6 +155,12 @@ namespace UnityEngine.UI.Extensions
SetVerticesDirty(); SetVerticesDirty();
} }
public void SetArc(float arc)
{
Arc = arc;
SetVerticesDirty();
}
public void SetArcSteps(int steps) public void SetArcSteps(int steps)
{ {
ArcSteps = steps; ArcSteps = steps;

View File

@ -111,10 +111,18 @@ namespace UnityEngine.UI.Extensions
set set
{ {
if (m_points == value) if (m_points == value) return;
return;
m_points = value; if (value == null || value.Length == 0)
SetAllDirty(); {
m_points = new Vector2[1];
}
else
{
m_points = value;
}
SetAllDirty();
} }
} }

View File

@ -0,0 +1,146 @@
/// Credit Steve Westhoff, jack.sydorenko, firagon
/// Sourced from - https://bitbucket.org/UnityUIExtensions/unity-ui-extensions/issues/324
/// Refactored and updated for performance from UILineRenderer by Steve Westhoff
using System.Collections.Generic;
namespace UnityEngine.UI.Extensions
{
[AddComponentMenu("UI/Extensions/Primitives/UILineRendererFIFO")]
[RequireComponent(typeof(RectTransform))]
public class UILineRendererFIFO : UIPrimitiveBase
{
private static readonly Vector2[] middleUvs = new[] { new Vector2(0.5f, 0), new Vector2(0.5f, 1), new Vector2(0.5f, 1), new Vector2(0.5f, 0) };
private List<Vector2> addedPoints = new List<Vector2>();
private bool needsResize;
[SerializeField, Tooltip("Thickness of the line")]
private float lineThickness = 1;
[SerializeField, Tooltip("Points to draw lines between\n Can be improved using the Resolution Option")]
private List<Vector2> points = new List<Vector2>();
[SerializeField, Tooltip("Segments to be drawn\n This is a list of arrays of points")]
private List<UIVertex[]> segments = new List<UIVertex[]>();
/// <summary>
/// Thickness of the line
/// </summary>
public float LineThickness
{
get { return lineThickness; }
set { lineThickness = value; SetAllDirty(); }
}
/// <summary>
/// Points to be drawn in the line.
/// </summary>
/// <remarks>Don't add points to the list directly, use the add / remove functions</remarks>
public List<Vector2> Points
{
get
{
return points;
}
set
{
if (points == value)
return;
points = value;
SetAllDirty();
}
}
/// <summary>
/// Adds to head
/// </summary>
/// <param name="point"></param>
public void AddPoint(Vector2 point) {
points.Add(point);
addedPoints.Add(point);
}
/// <summary>
/// Removes from tail (FIFO)
/// </summary>
public void RemovePoint() {
points.RemoveAt(0);
needsResize = true;
}
/// <summary>
/// Clear all the points from the LineRenderer
/// </summary>
public void ClearPoints()
{
segments.Clear();
points.Clear();
addedPoints.Clear();
needsResize = false;
}
public void Resize() {
needsResize = true;
}
protected override void OnPopulateMesh(VertexHelper vertexHelper) {
vertexHelper.Clear();
if(needsResize) {
needsResize = false;
segments.Clear();
addedPoints = new List<Vector2>(points);
}
int count = addedPoints.Count;
if(count > 1) {
PopulateMesh(addedPoints, vertexHelper);
if(count % 2 == 0) {
addedPoints.Clear();
} else {
Vector2 extraPoint = addedPoints[count - 1];
addedPoints.Clear();
addedPoints.Add(extraPoint);
}
}
}
void PopulateMesh(List<Vector2> pointsToDraw, VertexHelper vertexHelper) {
if(ImproveResolution != ResolutionMode.None) {
pointsToDraw = IncreaseResolution(pointsToDraw);
}
float sizeX = rectTransform.rect.width;
float sizeY = rectTransform.rect.height;
float offsetX = -rectTransform.pivot.x * sizeX;
float offsetY = -rectTransform.pivot.y * sizeY;
for(int i = 1; i < pointsToDraw.Count; i += 2) {
Vector2 start = pointsToDraw[i - 1];
Vector2 end = pointsToDraw[i];
start = new Vector2(start.x * sizeX + offsetX, start.y * sizeY + offsetY);
end = new Vector2(end.x * sizeX + offsetX, end.y * sizeY + offsetY);
UIVertex[] segment = CreateLineSegment(start, end, segments.Count > 1 ? segments[segments.Count - 2] : null);
segments.Add(segment);
}
for(int i = 0; i < segments.Count; i++) {
vertexHelper.AddUIVertexQuad(segments[i]);
}
if(vertexHelper.currentVertCount > 64000) {
Debug.LogError("Max Verticies size is 64000, current mesh vertcies count is [" + vertexHelper.currentVertCount + "] - Cannot Draw");
vertexHelper.Clear();
}
}
UIVertex[] CreateLineSegment(Vector2 start, Vector2 end, UIVertex[] previousVert = null) {
Vector2 offset = new Vector2(start.y - end.y, end.x - start.x).normalized * lineThickness * 0.5f;
Vector2 v1;
Vector2 v2;
if(previousVert != null) {
v1 = new Vector2(previousVert[3].position.x, previousVert[3].position.y);
v2 = new Vector2(previousVert[2].position.x, previousVert[2].position.y);
} else {
v1 = start - offset;
v2 = start + offset;
}
return SetVbo(new[] { v1, v2, end + offset, end - offset }, middleUvs);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a3a91607af301f241b9f0a860c720b21
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -118,24 +118,6 @@ namespace UnityEngine.UI.Extensions
} }
} }
// /// <summary>
// /// List of Segments to be drawn.
// /// </summary>
// public List<Vector2[]> Segments
//{
// get
// {
// return m_segments;
// }
// set
// {
// m_segments = value;
// SetAllDirty();
// }
//}
public void AddPoint(Vector2 pointToAdd) public void AddPoint(Vector2 pointToAdd)
{ {
m_points.Add(pointToAdd); m_points.Add(pointToAdd);
@ -298,15 +280,6 @@ namespace UnityEngine.UI.Extensions
PopulateMesh (vh, m_points); PopulateMesh (vh, m_points);
} }
//else if (m_segments != null && m_segments.Count > 0) {
// GeneratedUVs ();
// vh.Clear ();
// for (int s = 0; s < m_segments.Count; s++) {
// Vector2[] pointsToDraw = m_segments [s];
// PopulateMesh (vh, pointsToDraw);
// }
//}
} }
private UIVertex[] CreateLineCap(Vector2 start, Vector2 end, SegmentType type) private UIVertex[] CreateLineCap(Vector2 start, Vector2 end, SegmentType type)

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