From 75bb272c5e058927838052053d1f4b86d2080ca0 Mon Sep 17 00:00:00 2001 From: "Simon (Darkside) Jackson" Date: Wed, 27 May 2026 19:05:45 +0100 Subject: [PATCH 1/2] Batch 2 controls --- .github/CODEOWNERS | 1 + .github/FUNDING.yml | 6 + .github/ISSUE_TEMPLATE/bug_report.md | 44 + .github/ISSUE_TEMPLATE/feature_request.md | 30 + .../ISSUE_TEMPLATE/request_for_information.md | 30 + .github/PULL_REQUEST_TEMPLATE.md | 30 + .../development-buildandtestupmrelease.yml | 87 ++ .gitignore | 36 + .gitmodules | 3 + .npmignore | 38 + CHANGELOG.md | 9 + CHANGELOG.md.meta | 7 + .../Controls/CircularImageButton.md | 100 ++ Documentation~/Controls/CollapsibleSection.md | 97 ++ Documentation~/Controls/ColorToggleButton.md | 105 ++ Documentation~/Controls/ColorToggleGroup.md | 92 ++ Documentation~/Controls/ComingSoonMessage.md | 91 ++ Documentation~/Controls/GrayscaleImage.md | 92 ++ Documentation~/Controls/IconLabelButton.md | 99 ++ Documentation~/Controls/LoadingIcon.md | 98 ++ Documentation~/Controls/PageDotIndicator.md | 89 ++ Documentation~/Controls/PillButton.md | 101 ++ Documentation~/Controls/PillInputField.md | 113 ++ Documentation~/Controls/PillSelector.md | 101 ++ Documentation~/Controls/QuadrantStepper.md | 118 ++ Documentation~/Controls/RoundedInputField.md | 99 ++ Documentation~/Controls/StepProgressBar.md | 90 ++ .../Controls/ToastSwipeDismissManipulator.md | 111 ++ Documentation~/Controls/ToggleButton.md | 99 ++ Documentation~/Examples/ContentExplorer.md | 71 + Documentation~/Examples/ProfileEditor.md | 59 + Documentation~/Examples/RegistrationForm.md | 73 + Documentation~/Examples/ScrollSnapAndDots.md | 50 + Documentation~/Examples/StepWizard.md | 67 + Documentation~/Examples/ToastNotifications.md | 77 + Documentation~/ScrollSnap.md | 220 +++ .../Utilities/ProceduralTextureUtility.md | 137 ++ .../Utilities/UIToolkitExtensions.md | 153 ++ Documentation~/VisualElementShakeUtility.md | 70 + .../com.unity.uitoolkiyextensions.md | 97 ++ Editor.meta | 8 + Editor/DumbScript.cs | 16 + Editor/DumbScript.cs.meta | 2 + Editor/UnityUIToolkitExtensions.Editor.asmdef | 18 + ...nityUIToolkitExtensions.Editor.asmdef.meta | 7 + Examples~ | 1 + LICENSE | 35 +- LICENSE.meta | 7 + README.md | 90 +- README.md.meta | 7 + Runtime.meta | 8 + Runtime/Assets.meta | 8 + Runtime/Assets/Icons.meta | 8 + Runtime/Assets/Icons/Camera.svg | 13 + Runtime/Assets/Icons/Camera.svg.meta | 53 + Runtime/Assets/Icons/ChevronDown.svg | 8 + Runtime/Assets/Icons/ChevronDown.svg.meta | 53 + Runtime/Assets/Icons/ChevronRight.svg | 8 + Runtime/Assets/Icons/ChevronRight.svg.meta | 53 + Runtime/Assets/Icons/ListItem.svg | 9 + Runtime/Assets/Icons/ListItem.svg.meta | 53 + Runtime/Assets/Icons/LoadingSpinner.svg | 10 + Runtime/Assets/Icons/LoadingSpinner.svg.meta | 53 + Runtime/Controls.meta | 8 + Runtime/Controls/CircularImageButton.meta | 8 + .../CircularImageButton.cs | 105 ++ .../CircularImageButton.cs.meta | 2 + Runtime/Controls/CollapsibleSection.meta | 8 + .../CollapsibleSection/CollapsibleSection.cs | 95 ++ .../CollapsibleSection.cs.meta | 2 + Runtime/Controls/ColorToggleButton.meta | 8 + .../ColorToggleButton/ColorToggleButton.cs | 133 ++ .../ColorToggleButton.cs.meta | 2 + Runtime/Controls/ColorToggleGroup.meta | 8 + .../ColorToggleGroup/ColorToggleGroup.cs | 249 ++++ .../ColorToggleGroup/ColorToggleGroup.cs.meta | 2 + Runtime/Controls/ComingSoonMessage.meta | 8 + .../ComingSoonMessage/ComingSoonMessage.cs | 43 + .../ComingSoonMessage.cs.meta | 2 + Runtime/Controls/GrayscaleImage.meta | 8 + .../Controls/GrayscaleImage/GrayscaleImage.cs | 253 ++++ .../GrayscaleImage/GrayscaleImage.cs.meta | 2 + Runtime/Controls/IconLabelButton.meta | 8 + .../IconLabelButton/IconLabelButton.cs | 74 + .../IconLabelButton/IconLabelButton.cs.meta | 2 + Runtime/Controls/LoadingIcon.meta | 8 + Runtime/Controls/LoadingIcon/LoadingIcon.cs | 94 ++ .../Controls/LoadingIcon/LoadingIcon.cs.meta | 2 + Runtime/Controls/PageDotIndicator.meta | 8 + .../PageDotIndicator/PageDotIndicator.cs | 106 ++ .../PageDotIndicator/PageDotIndicator.cs.meta | 2 + Runtime/Controls/PillButton.meta | 8 + Runtime/Controls/PillButton/PillButton.cs | 147 ++ .../Controls/PillButton/PillButton.cs.meta | 2 + Runtime/Controls/PillInputField.meta | 8 + .../Controls/PillInputField/PillInputField.cs | 219 +++ .../PillInputField/PillInputField.cs.meta | 2 + Runtime/Controls/PillSelector.meta | 8 + Runtime/Controls/PillSelector/PillSelector.cs | 66 + .../PillSelector/PillSelector.cs.meta | 2 + Runtime/Controls/RoundedInputField.meta | 8 + .../RoundedInputField/RoundedInputField.cs | 145 ++ .../RoundedInputField.cs.meta | 2 + Runtime/Controls/ScrollSnap.meta | 8 + Runtime/Controls/ScrollSnap/ScrollSnap.cs | 1263 +++++++++++++++++ .../Controls/ScrollSnap/ScrollSnap.cs.meta | 2 + Runtime/Controls/StepProgressBar.meta | 8 + .../StepProgressBar/StepProgressBar.cs | 101 ++ .../StepProgressBar/StepProgressBar.cs.meta | 2 + Runtime/Controls/Stepper.meta | 8 + Runtime/Controls/Stepper/QuadrantStepper.cs | 377 +++++ .../Controls/Stepper/QuadrantStepper.cs.meta | 2 + .../ToastSwipeDismissManipulator.meta | 8 + .../ToastSwipeDismissManipulator.cs | 503 +++++++ .../ToastSwipeDismissManipulator.cs.meta | 2 + Runtime/Controls/ToggleButton.meta | 8 + Runtime/Controls/ToggleButton/ToggleButton.cs | 88 ++ .../ToggleButton/ToggleButton.cs.meta | 2 + Runtime/Extensions.meta | 8 + Runtime/Extensions/UIToolkitExtensions.cs | 171 +++ .../Extensions/UIToolkitExtensions.cs.meta | 2 + Runtime/Styles.meta | 8 + .../UIToolkitExtensionsControlStyles.uss | 435 ++++++ .../UIToolkitExtensionsControlStyles.uss.meta | 11 + Runtime/UnityUIToolkitExtensions.asmdef | 16 + Runtime/UnityUIToolkitExtensions.asmdef.meta | 7 + Runtime/Utility.meta | 8 + Runtime/Utility/ProceduralTextureUtility.cs | 108 ++ Runtime/Utility/VisualElementShakeUtility.cs | 193 +++ .../Utility/VisualElementShakeUtility.cs.meta | 2 + package.json | 39 + package.json.meta | 7 + 132 files changed, 8864 insertions(+), 18 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/request_for_information.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/development-buildandtestupmrelease.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .npmignore create mode 100644 CHANGELOG.md create mode 100644 CHANGELOG.md.meta create mode 100644 Documentation~/Controls/CircularImageButton.md create mode 100644 Documentation~/Controls/CollapsibleSection.md create mode 100644 Documentation~/Controls/ColorToggleButton.md create mode 100644 Documentation~/Controls/ColorToggleGroup.md create mode 100644 Documentation~/Controls/ComingSoonMessage.md create mode 100644 Documentation~/Controls/GrayscaleImage.md create mode 100644 Documentation~/Controls/IconLabelButton.md create mode 100644 Documentation~/Controls/LoadingIcon.md create mode 100644 Documentation~/Controls/PageDotIndicator.md create mode 100644 Documentation~/Controls/PillButton.md create mode 100644 Documentation~/Controls/PillInputField.md create mode 100644 Documentation~/Controls/PillSelector.md create mode 100644 Documentation~/Controls/QuadrantStepper.md create mode 100644 Documentation~/Controls/RoundedInputField.md create mode 100644 Documentation~/Controls/StepProgressBar.md create mode 100644 Documentation~/Controls/ToastSwipeDismissManipulator.md create mode 100644 Documentation~/Controls/ToggleButton.md create mode 100644 Documentation~/Examples/ContentExplorer.md create mode 100644 Documentation~/Examples/ProfileEditor.md create mode 100644 Documentation~/Examples/RegistrationForm.md create mode 100644 Documentation~/Examples/ScrollSnapAndDots.md create mode 100644 Documentation~/Examples/StepWizard.md create mode 100644 Documentation~/Examples/ToastNotifications.md create mode 100644 Documentation~/ScrollSnap.md create mode 100644 Documentation~/Utilities/ProceduralTextureUtility.md create mode 100644 Documentation~/Utilities/UIToolkitExtensions.md create mode 100644 Documentation~/VisualElementShakeUtility.md create mode 100644 Documentation~/com.unity.uitoolkiyextensions.md create mode 100644 Editor.meta create mode 100644 Editor/DumbScript.cs create mode 100644 Editor/DumbScript.cs.meta create mode 100644 Editor/UnityUIToolkitExtensions.Editor.asmdef create mode 100644 Editor/UnityUIToolkitExtensions.Editor.asmdef.meta create mode 160000 Examples~ create mode 100644 LICENSE.meta create mode 100644 README.md.meta create mode 100644 Runtime.meta create mode 100644 Runtime/Assets.meta create mode 100644 Runtime/Assets/Icons.meta create mode 100644 Runtime/Assets/Icons/Camera.svg create mode 100644 Runtime/Assets/Icons/Camera.svg.meta create mode 100644 Runtime/Assets/Icons/ChevronDown.svg create mode 100644 Runtime/Assets/Icons/ChevronDown.svg.meta create mode 100644 Runtime/Assets/Icons/ChevronRight.svg create mode 100644 Runtime/Assets/Icons/ChevronRight.svg.meta create mode 100644 Runtime/Assets/Icons/ListItem.svg create mode 100644 Runtime/Assets/Icons/ListItem.svg.meta create mode 100644 Runtime/Assets/Icons/LoadingSpinner.svg create mode 100644 Runtime/Assets/Icons/LoadingSpinner.svg.meta create mode 100644 Runtime/Controls.meta create mode 100644 Runtime/Controls/CircularImageButton.meta create mode 100644 Runtime/Controls/CircularImageButton/CircularImageButton.cs create mode 100644 Runtime/Controls/CircularImageButton/CircularImageButton.cs.meta create mode 100644 Runtime/Controls/CollapsibleSection.meta create mode 100644 Runtime/Controls/CollapsibleSection/CollapsibleSection.cs create mode 100644 Runtime/Controls/CollapsibleSection/CollapsibleSection.cs.meta create mode 100644 Runtime/Controls/ColorToggleButton.meta create mode 100644 Runtime/Controls/ColorToggleButton/ColorToggleButton.cs create mode 100644 Runtime/Controls/ColorToggleButton/ColorToggleButton.cs.meta create mode 100644 Runtime/Controls/ColorToggleGroup.meta create mode 100644 Runtime/Controls/ColorToggleGroup/ColorToggleGroup.cs create mode 100644 Runtime/Controls/ColorToggleGroup/ColorToggleGroup.cs.meta create mode 100644 Runtime/Controls/ComingSoonMessage.meta create mode 100644 Runtime/Controls/ComingSoonMessage/ComingSoonMessage.cs create mode 100644 Runtime/Controls/ComingSoonMessage/ComingSoonMessage.cs.meta create mode 100644 Runtime/Controls/GrayscaleImage.meta create mode 100644 Runtime/Controls/GrayscaleImage/GrayscaleImage.cs create mode 100644 Runtime/Controls/GrayscaleImage/GrayscaleImage.cs.meta create mode 100644 Runtime/Controls/IconLabelButton.meta create mode 100644 Runtime/Controls/IconLabelButton/IconLabelButton.cs create mode 100644 Runtime/Controls/IconLabelButton/IconLabelButton.cs.meta create mode 100644 Runtime/Controls/LoadingIcon.meta create mode 100644 Runtime/Controls/LoadingIcon/LoadingIcon.cs create mode 100644 Runtime/Controls/LoadingIcon/LoadingIcon.cs.meta create mode 100644 Runtime/Controls/PageDotIndicator.meta create mode 100644 Runtime/Controls/PageDotIndicator/PageDotIndicator.cs create mode 100644 Runtime/Controls/PageDotIndicator/PageDotIndicator.cs.meta create mode 100644 Runtime/Controls/PillButton.meta create mode 100644 Runtime/Controls/PillButton/PillButton.cs create mode 100644 Runtime/Controls/PillButton/PillButton.cs.meta create mode 100644 Runtime/Controls/PillInputField.meta create mode 100644 Runtime/Controls/PillInputField/PillInputField.cs create mode 100644 Runtime/Controls/PillInputField/PillInputField.cs.meta create mode 100644 Runtime/Controls/PillSelector.meta create mode 100644 Runtime/Controls/PillSelector/PillSelector.cs create mode 100644 Runtime/Controls/PillSelector/PillSelector.cs.meta create mode 100644 Runtime/Controls/RoundedInputField.meta create mode 100644 Runtime/Controls/RoundedInputField/RoundedInputField.cs create mode 100644 Runtime/Controls/RoundedInputField/RoundedInputField.cs.meta create mode 100644 Runtime/Controls/ScrollSnap.meta create mode 100644 Runtime/Controls/ScrollSnap/ScrollSnap.cs create mode 100644 Runtime/Controls/ScrollSnap/ScrollSnap.cs.meta create mode 100644 Runtime/Controls/StepProgressBar.meta create mode 100644 Runtime/Controls/StepProgressBar/StepProgressBar.cs create mode 100644 Runtime/Controls/StepProgressBar/StepProgressBar.cs.meta create mode 100644 Runtime/Controls/Stepper.meta create mode 100644 Runtime/Controls/Stepper/QuadrantStepper.cs create mode 100644 Runtime/Controls/Stepper/QuadrantStepper.cs.meta create mode 100644 Runtime/Controls/ToastSwipeDismissManipulator.meta create mode 100644 Runtime/Controls/ToastSwipeDismissManipulator/ToastSwipeDismissManipulator.cs create mode 100644 Runtime/Controls/ToastSwipeDismissManipulator/ToastSwipeDismissManipulator.cs.meta create mode 100644 Runtime/Controls/ToggleButton.meta create mode 100644 Runtime/Controls/ToggleButton/ToggleButton.cs create mode 100644 Runtime/Controls/ToggleButton/ToggleButton.cs.meta create mode 100644 Runtime/Extensions.meta create mode 100644 Runtime/Extensions/UIToolkitExtensions.cs create mode 100644 Runtime/Extensions/UIToolkitExtensions.cs.meta create mode 100644 Runtime/Styles.meta create mode 100644 Runtime/Styles/UIToolkitExtensionsControlStyles.uss create mode 100644 Runtime/Styles/UIToolkitExtensionsControlStyles.uss.meta create mode 100644 Runtime/UnityUIToolkitExtensions.asmdef create mode 100644 Runtime/UnityUIToolkitExtensions.asmdef.meta create mode 100644 Runtime/Utility.meta create mode 100644 Runtime/Utility/ProceduralTextureUtility.cs create mode 100644 Runtime/Utility/VisualElementShakeUtility.cs create mode 100644 Runtime/Utility/VisualElementShakeUtility.cs.meta create mode 100644 package.json create mode 100644 package.json.meta diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..83f1107 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* SimonDarksideJ \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..3e56381 --- /dev/null +++ b/.github/FUNDING.yml @@ -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 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..702a082 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,44 @@ +--- +name: Bug report +about: Create a report to identify a potential issue +title: 'BUG: ' +labels: bug +assignees: '' +--- + +## Unity UI Toolkit Extensions Bug Report + +## Describe the bug + + +## To Reproduce + + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +## Expected behavior + + +## Actual behavior + + +## Screenshots + + +## Your Setup + + +- **Operation System:** + - [ ] Windows + - [ ] MacOs + - [ ] Linux + - [ ] Other +- **Unity Version:** 6000.64f1f +- **Unity UI Toolkit Extensions Version** + - [ ] 1.0.0 + +## Additional context + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..04f5e1c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,30 @@ +--- +name: Feature request +about: Suggest an idea for the Unity UI Toolkit Extensions +title: 'Feature Request: ' +labels: enhancement +assignees: '' +--- + +## Unity UI Toolkit Extensions Feature Request + +## Is your feature request related to a problem? Please describe + + + +## How would you classify your suggestion + + +- New platform support +- Usability / Configuration +- Architecture / Services + +## Describe the solution you'd like + + +## Describe alternatives you've considered + + +## Additional context + diff --git a/.github/ISSUE_TEMPLATE/request_for_information.md b/.github/ISSUE_TEMPLATE/request_for_information.md new file mode 100644 index 0000000..abe29ca --- /dev/null +++ b/.github/ISSUE_TEMPLATE/request_for_information.md @@ -0,0 +1,30 @@ +--- +name: Request for Information +about: Not sure how to do something, just ask. +title: 'RFI: ' +labels: question +assignees: '' +--- + +## Unity UI Toolkit Extensions Request for Information + + + +## What are you trying to achieve? + + + +## What have you already tried + + + +## What material have you referenced that didn't answer your question + + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..0701105 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,30 @@ +# Unity UI Toolkit Extensions - Pull Request + +## Overview + + +## Changes + + +- Fixes: + +## Breaking Changes + + +- Breaks + +## Related Submodule Changes + + +- URL + +## Testing status + +* No tests have been added. +* Includes unit tests. +* Includes performance tests. +* Includes integration tests. + +### Manual testing status + + diff --git a/.github/workflows/development-buildandtestupmrelease.yml b/.github/workflows/development-buildandtestupmrelease.yml new file mode 100644 index 0000000..5d726a9 --- /dev/null +++ b/.github/workflows/development-buildandtestupmrelease.yml @@ -0,0 +1,87 @@ +name: Build and test UPM packages for platforms, all branches except main + +on: + pull_request: + branches-ignore: + - 'main' + # Ignore PRs targeting main + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true + +# Ensure default token scopes and inherit org-level secrets via env mapping +permissions: + contents: write + packages: read + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GIT_PAT: ${{ secrets.GIT_PAT }} + +jobs: + test-unity-build: + name: Test Unity UPM Build + runs-on: ${{ matrix.os }} + if: always() + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + unity-version: + - 6000.0.x + - 6000 + include: + - os: ubuntu-latest + build-targets: StandaloneLinux64, Android + - os: windows-latest + build-targets: StandaloneWindows64 + - os: macos-latest + build-targets: StandaloneOSX, iOS + steps: + - uses: buildalon/unity-setup@v2 + id: unity-setup + with: + version-file: "None" + unity-version: ${{ matrix.unity-version }} # overrides version in version-file + build-targets: ${{ matrix.build-targets }} + + - run: | + echo "Step Outputs:" + echo "steps.unity-setup.unity-hub-path: '${{ steps.unity-setup.outputs.unity-hub-path }}'" + echo "steps.unity-setup.unity-editors: '${{ steps.unity-setup.outputs.unity-editors }}'" + echo "steps.unity-setup.unity-editor-path: '${{ steps.unity-setup.outputs.unity-editor-path }}'" + echo "steps.unity-setup.unity-project-path: '${{ steps.unity-setup.outputs.unity-project-path }}'" + + echo "Environment Variables:" + echo "UNITY_HUB_PATH: '${{ env.UNITY_HUB_PATH }}'" + echo "UNITY_EDITORS: '${{ env.UNITY_EDITORS }}'" + echo "UNITY_EDITOR_PATH: '${{ env.UNITY_EDITOR_PATH }}'" + echo "UNITY_PROJECT_PATH: '${{ env.UNITY_PROJECT_PATH }}'" + + - uses: buildalon/activate-unity-license@v2 + if: runner.environment == 'github-hosted' + with: + license: "Personal" # Choose license type to use [ Personal, Professional, Floating ] + username: ${{ secrets.UNITY_USER}} + password: ${{ secrets.UNITY_ACC}} + + - uses: buildalon/create-unity-project@v2 + id: create-unity-project + with: + project-name: P + + - name: Setup Unity UPM Build + uses: realitycollective/reality-collective-actions/setup-unity-upm-build@v1 + with: + unity-project-path: ${{ steps.create-unity-project.outputs.project-path }} + + - name: Run Unity Build + uses: realitycollective/reality-collective-actions/run-unity-multitarget-build@v1 + with: + unity-editor-path: ${{ steps.unity-setup.outputs.unity-editor-path }} + unity-project-path: ${{ steps.create-unity-project.outputs.project-path }} + build-targets: ${{ matrix.build-targets }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c378a50 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +**/[Ll]ibrary/ +**/[Tt]emp/ +**/[Oo]bj/ +**/[Bb]uild/ +**/[Bb]uilds/ +**/[Ee]xport +**/Assets/AssetStoreTools* +# Autogenerated VS/MD/Consulo solution and project files +ExportedObj/ +.consulo/ +*.csproj +*.unityproj +*.sln +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd + +# Unity3D generated meta files +*.pidb.meta + +# Mac files +*.DS_Store + +# Unity3D Generated File On Crash Reports +sysinfo.txt + +# Builds +*.apk +*.unitypackage +/.vs + +**/node_modules/* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2716903 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Examples~"] + path = Examples~ + url = https://github.com/Unity-UI-Extensions/com.unity.uitoolkitextensions.examples.git diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..f2b359f --- /dev/null +++ b/.npmignore @@ -0,0 +1,38 @@ +**/[Ll]ibrary/ +**/[Tt]emp/ +**/[Oo]bj/ +**/[Bb]uild/ +**/[Bb]uilds/ +**/[Ee]xport +**/Assets/AssetStoreTools* + +# Autogenerated VS/MD/Consulo solution and project files +ExportedObj/ +.consulo/ +*.csproj +*.unityproj +*.sln +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd + +# Unity3D generated meta files +*.pidb.meta + +# Mac files +*.DS_Store + +# Unity3D Generated File On Crash Reports +sysinfo.txt + +# Builds +*.apk +*.unitypackage +/.vs +*.gitmodules + +**/.github/* \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..14b74ab --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +# Change Log + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/). + +## Release 1.0.0 - Launch - tbc + +Coming Soon. diff --git a/CHANGELOG.md.meta b/CHANGELOG.md.meta new file mode 100644 index 0000000..9eafa9c --- /dev/null +++ b/CHANGELOG.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9906385307c15b94c85faf27bb6467b3 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Documentation~/Controls/CircularImageButton.md b/Documentation~/Controls/CircularImageButton.md new file mode 100644 index 0000000..2b76b42 --- /dev/null +++ b/Documentation~/Controls/CircularImageButton.md @@ -0,0 +1,100 @@ +# CircularImageButton + +## Summary + +`CircularImageButton` is a circular button that displays an image or sprite at full bleed with a 50% border-radius. When no image is set it shows a centered overlay (typically an upload icon and label) to prompt the user to select a photo. + +Typical use cases: + +- Avatar display and photo selection in profile screens +- Circular media thumbnails in lists or grids +- Any tap target that must hold a user-supplied image + +## Properties + +This control has no data properties. Configure it through method calls and respond to the `Clicked` event. + +## USS Classes + +| Class | Description | +| --- | --- | +| `circularImageButton` | Root element. Applies circular clip via `border-radius: 50%`. | +| `circularImageButton__image` | The image layer. Absolutely positioned, fills the button, `border-radius: 50%`. | +| `circularImageButton__noImageOverlay` | Overlay shown when no image is set. Absolutely positioned and centered. | +| `circularImageButton__icon` | Icon inside the no-image overlay (typically an upload/camera glyph). | +| `circularImageButton__uploadLabel` | Text label inside the no-image overlay. | +| `circularImageButton--hasImage` | Modifier applied to the root when an image is present. Hides the no-image overlay. | + +## Events + +| Name | Description | Arguments | +| --- | --- | --- | +| `Clicked` | Fired when the button receives a pointer-up event inside its bounds. | none | + +## Public Methods + +| Signature | Description | +| --- | --- | +| `SetImage(Texture2D texture, bool isDefault = false)` | Sets the displayed image from a `Texture2D`. Pass `isDefault = true` to treat the image as a placeholder (does not apply the `--hasImage` modifier). | +| `SetImage(Sprite sprite, bool isDefault = false)` | Sets the displayed image from a `Sprite`. Same `isDefault` semantics. | +| `SetUploadLabel(string text)` | Updates the text shown inside the no-image overlay. | +| `ClearImage()` | Removes the current image and restores the no-image overlay. | +| `SetImageTint(Color color)` | Applies a tint color to the image element. | + +## Using the Control + +### Basic Setup + +```csharp +using UnityEngine; +using UnityEngine.UIElements; +using UnityUIToolkit.Extensions; + +public class AvatarController : MonoBehaviour +{ + [SerializeField] private UIDocument _document; + [SerializeField] private Texture2D _defaultAvatar; + + private CircularImageButton _avatarButton; + + private void OnEnable() + { + var root = _document.rootVisualElement; + _avatarButton = new CircularImageButton(); + + // Show a default placeholder image without hiding the overlay + _avatarButton.SetImage(_defaultAvatar, isDefault: true); + _avatarButton.SetUploadLabel("Tap to change"); + + _avatarButton.Clicked += OnAvatarTapped; + root.Add(_avatarButton); + } + + private void OnAvatarTapped() + { + // Open native photo picker, then call SetImage with the result + Debug.Log("Avatar tapped — open photo picker"); + } + + private void ApplyPickedPhoto(Texture2D picked) + { + // isDefault: false — hides the overlay and marks the button as having an image + _avatarButton.SetImage(picked, isDefault: false); + } + + private void ResetAvatar() + { + _avatarButton.ClearImage(); + } +} +``` + +### Tint and Dynamic Color + +```csharp +// Grey-out the avatar when the profile is locked +_avatarButton.SetImageTint(new Color(1f, 1f, 1f, 0.4f)); + +// Restore full color +_avatarButton.SetImageTint(Color.white); +``` diff --git a/Documentation~/Controls/CollapsibleSection.md b/Documentation~/Controls/CollapsibleSection.md new file mode 100644 index 0000000..79ee6be --- /dev/null +++ b/Documentation~/Controls/CollapsibleSection.md @@ -0,0 +1,97 @@ +# CollapsibleSection + +## Summary + +`CollapsibleSection` is a container with a tappable header that expands or collapses its body content. The body transition is driven by a `max-height` animation (0 → 2000 px, 250 ms ease-out) triggered by the `collapsibleSection--expanded` modifier class, so no code-side animation is required for the open/close motion. + +Typical use cases: + +- FAQ accordion panels +- Collapsible settings groups +- Nested content trees inside scroll containers +- Any section where body content should be hidden by default + +## Properties + +| Name | Description | Options | +| --- | --- | --- | +| `IsExpanded` | Gets or sets the current expanded state. Setting this value animates the body and fires `OnExpandedChanged`. | `bool` | +| `TitleText` | Gets or sets the header label text. | `string` | + +## USS Classes + +| Class | Description | +| --- | --- | +| `collapsibleSection` | Root element. | +| `collapsibleSection__header` | Tappable header row. Contains the title and chevron. | +| `collapsibleSection__title` | Label element inside the header. | +| `collapsibleSection__chevron` | Chevron/arrow icon that rotates to indicate state. | +| `collapsibleSection__body` | Outer body wrapper. Has `max-height` transition for the open/close animation. | +| `collapsibleSection__bodyContent` | Inner content container. Receives children added via `AddBodyContent`. | +| `collapsibleSection--expanded` | Modifier applied to the root when expanded. Drives the `max-height` transition and chevron rotation. | + +## Events + +| Name | Description | Arguments | +| --- | --- | --- | +| `OnExpandedChanged` | Fired after the expanded state changes. | `bool isExpanded` | + +## Public Methods + +| Signature | Description | +| --- | --- | +| `AddBodyContent(VisualElement element)` | Appends a child element to the inner body content container. | +| `SetBodyText(string text) : Label` | Convenience method that creates and appends a `Label` with the given text. Returns the created label. | +| `Toggle()` | Toggles the expanded state. Equivalent to `IsExpanded = !IsExpanded`. | + +## Using the Control + +### Basic Setup + +```csharp +using UnityEngine; +using UnityEngine.UIElements; +using UnityUIToolkit.Extensions; + +public class FaqController : MonoBehaviour +{ + [SerializeField] private UIDocument _document; + + private void OnEnable() + { + var root = _document.rootVisualElement; + + var section = new CollapsibleSection(); + section.TitleText = "What is this app?"; + + // Add plain text body + section.SetBodyText( + "This app helps you track your daily habits and review progress over time."); + + // Add a richer body element + var linkLabel = new Label("Learn more at example.com"); + linkLabel.style.color = new StyleColor(new Color(0.35f, 0.65f, 1f)); + section.AddBodyContent(linkLabel); + + section.OnExpandedChanged += isExpanded => + { + Debug.Log($"Section is now {(isExpanded ? "open" : "closed")}"); + }; + + root.Add(section); + } +} +``` + +### Programmatic Expand / Collapse + +```csharp +// Open all sections on first visit +foreach (var section in _faqSections) +{ + section.IsExpanded = true; +} + +// Toggle a section from an external button +_toggleButton.clicked += () => _detailsSection.Toggle(); +``` diff --git a/Documentation~/Controls/ColorToggleButton.md b/Documentation~/Controls/ColorToggleButton.md new file mode 100644 index 0000000..0170144 --- /dev/null +++ b/Documentation~/Controls/ColorToggleButton.md @@ -0,0 +1,105 @@ +# ColorToggleButton + +## Summary + +`ColorToggleButton` extends `ToggleButton` with per-instance tint colors, a ripple press animation, and a selection overlay. The background color is driven entirely by the tint values, making it straightforward to build color-coded toggle grids without USS variants. + +Typical use cases: + +- Color picker palette items +- Labeled color-coded category toggles +- Any toggle grid where each item has a distinct brand or theme color + +## Properties + +| Name | Description | Options | +| --- | --- | --- | +| `IsSelected` | Inherited from `ToggleButton`. Gets or sets the selected state. | `bool` | +| `TintColor` | The background tint used when the button is in its default (unselected) state. | `Color` (read-only; use `SetTintColor` to change) | +| `SelectedTintColor` | The background tint used when the button is selected. | `Color` (read-only; use `SetSelectedTintColor` to change) | + +## USS Classes + +| Class | Description | +| --- | --- | +| `toggleButton` | Root element (inherited from `ToggleButton`). | +| `toggleButton__image` | Image layer, 100% size, scale-to-fit (inherited). | +| `toggleButton--selected` | Modifier applied when selected (inherited). | +| `toggleButton__icon` | Optional icon element overlaid on the colored background. | +| `toggleButton__ripple` | Primary ripple circle that expands on press. | +| `toggleButton__rippleSecondary` | Secondary ripple circle for the layered ripple effect. | +| `toggleButton__selectedOverlay` | Overlay element shown when selected. | +| `toggleButton__selectedOverlay--visible` | Modifier that makes the selected overlay visible. | + +## Events + +| Name | Description | Arguments | +| --- | --- | --- | +| `OnClicked` | Inherited from `ToggleButton`. Fired on every pointer-down regardless of current state. | none | + +## Constructors + +| Signature | Description | +| --- | --- | +| `ColorToggleButton(Color tintColor)` | Creates a button with the same tint color for both selected and unselected states. | +| `ColorToggleButton(Color tintColor, Color selectedTintColor)` | Creates a button with distinct tints for each state. | + +## Public Methods + +| Signature | Description | +| --- | --- | +| `SetTintColor(Color color)` | Updates the unselected background tint at runtime. | +| `SetSelectedTintColor(Color color)` | Updates the selected background tint at runtime. | +| `SetImage(Texture2D texture)` | Inherited. Sets the icon/image on the button. | +| `ForceSelect()` | Inherited. Sets `IsSelected = true` without firing `OnClicked`. | +| `ForceDeselect()` | Inherited. Sets `IsSelected = false` without firing `OnClicked`. | + +## Using the Control + +### Color Palette Grid + +```csharp +using UnityEngine; +using UnityEngine.UIElements; +using UnityUIToolkit.Extensions; + +public class ColorPaletteController : MonoBehaviour +{ + [SerializeField] private UIDocument _document; + + private static readonly Color[] PaletteColors = new[] + { + new Color(0.91f, 0.27f, 0.38f), // red + new Color(0.25f, 0.56f, 0.96f), // blue + new Color(0.18f, 0.80f, 0.44f), // green + new Color(0.98f, 0.75f, 0.18f), // yellow + }; + + private ColorToggleButton _activeButton; + + private void OnEnable() + { + var root = _document.rootVisualElement; + var row = root.Q("paletteRow"); + + foreach (var color in PaletteColors) + { + // Slightly lighter shade for selected state + var selectedColor = Color.Lerp(color, Color.white, 0.25f); + var btn = new ColorToggleButton(color, selectedColor); + + btn.OnClicked += () => + { + // Deselect previous + if (_activeButton != null && _activeButton != btn) + _activeButton.ForceDeselect(); + + _activeButton = btn; + Debug.Log($"Selected color: {color}"); + }; + + row.Add(btn); + } + } +} +``` diff --git a/Documentation~/Controls/ColorToggleGroup.md b/Documentation~/Controls/ColorToggleGroup.md new file mode 100644 index 0000000..e2d1de4 --- /dev/null +++ b/Documentation~/Controls/ColorToggleGroup.md @@ -0,0 +1,92 @@ +# ColorToggleGroup + +## Summary + +`ColorToggleGroup` manages a set of `ColorToggleButton` items as a single-selection group. It supports both tap-to-select and drag-to-select gestures, ensuring that only one color is selected at a time. Selection state is coordinated internally; consumers only need to respond to `OnColorSelected`. + +Typical use cases: + +- Full-screen or inline color pickers +- Theme or accent color selectors +- Tag or category color selectors + +## Properties + +| Name | Description | Options | +| --- | --- | --- | +| `Colors` | Gets or sets the array of colors represented by the group. Changing this value rebuilds all child buttons. | `Color[]` | +| `SelectedColor` | Gets the currently selected color. `null` if nothing is selected. | `Color?` (nullable) | +| `Alignment` | Controls the flex direction of the buttons container. | `FlexDirection` | + +## USS Classes + +| Class | Description | +| --- | --- | +| `colorToggleGroup` | Root element. | +| `colorToggleGroup__container` | Flex container that holds all `ColorToggleButton` children. | + +## Events + +| Name | Description | Arguments | +| --- | --- | --- | +| `OnColorSelected` | Fired when the user selects a color by tap or drag. Not fired when selection changes programmatically via `SelectColor(color, propagateEvent: false)`. | `Color selectedColor` | + +## Public Methods + +| Signature | Description | +| --- | --- | +| `DeselectAll()` | Clears the current selection without firing `OnColorSelected`. | +| `SelectColor(Color color, bool propagateEvent = true)` | Programmatically selects the button whose color matches. Pass `propagateEvent: false` to suppress `OnColorSelected`. | + +## Using the Control + +### Inline Color Picker + +```csharp +using UnityEngine; +using UnityEngine.UIElements; +using UnityUIToolkit.Extensions; + +public class ThemeSelectorController : MonoBehaviour +{ + [SerializeField] private UIDocument _document; + + private ColorToggleGroup _colorGroup; + private Color _currentThemeColor = Color.white; + + private void OnEnable() + { + var root = _document.rootVisualElement; + + _colorGroup = new ColorToggleGroup(); + _colorGroup.Colors = new[] + { + new Color(0.91f, 0.27f, 0.38f), + new Color(0.25f, 0.56f, 0.96f), + new Color(0.18f, 0.80f, 0.44f), + new Color(0.98f, 0.75f, 0.18f), + new Color(0.60f, 0.20f, 0.80f), + }; + _colorGroup.Alignment = FlexDirection.Row; + + _colorGroup.OnColorSelected += OnThemeColorPicked; + + root.Q("colorPickerContainer").Add(_colorGroup); + + // Pre-select the saved theme color without firing the event + _colorGroup.SelectColor(_currentThemeColor, propagateEvent: false); + } + + private void OnThemeColorPicked(Color color) + { + _currentThemeColor = color; + Debug.Log($"Theme color changed to {color}"); + // Apply color to your UI here + } + + private void ResetSelection() + { + _colorGroup.DeselectAll(); + } +} +``` diff --git a/Documentation~/Controls/ComingSoonMessage.md b/Documentation~/Controls/ComingSoonMessage.md new file mode 100644 index 0000000..1e267d3 --- /dev/null +++ b/Documentation~/Controls/ComingSoonMessage.md @@ -0,0 +1,91 @@ +# ComingSoonMessage + +## Summary + +`ComingSoonMessage` is a simple full-area placeholder element that fills its container and communicates that a feature or screen is not yet available. It renders a background layer, a prominent title, and a descriptive message label. + +Typical use cases: + +- Placeholder screens during development or staged rollouts +- Stub pages inside a `ScrollSnap` onboarding flow +- In-progress feature sections within a settings or navigation layout + +## Properties + +| Name | Description | Options | +| --- | --- | --- | +| `Title` | Gets or sets the large heading text. | `string` | + +## USS Classes + +| Class | Description | +| --- | --- | +| `comingSoonMessage` | Root element. Fills its parent and centers its contents. | +| `comingSoonMessage__background` | Decorative background layer (may carry color or texture). | +| `comingSoonMessage__title` | Large heading label. | +| `comingSoonMessage__label` | Secondary descriptive message label below the title. | + +## Events + +This control does not emit events. + +## Public Methods + +| Signature | Description | +| --- | --- | +| `SetMessage(string text)` | Sets the secondary descriptive message shown below the title. | + +## Using the Control + +### Stub Page in a ScrollSnap + +```csharp +using UnityEngine; +using UnityEngine.UIElements; +using UnityUIToolkit.Extensions; + +public class OnboardingController : MonoBehaviour +{ + [SerializeField] private UIDocument _document; + + private void OnEnable() + { + var root = _document.rootVisualElement; + var scrollSnap = root.Q("onboardingSnap"); + + // Page 1 — real content + var welcomePage = new VisualElement(); + welcomePage.AddToClassList("sample-page"); + welcomePage.AddToClassList("sample-page--blue"); + scrollSnap.Add(welcomePage); + + // Page 2 — placeholder + var placeholder = new ComingSoonMessage(); + placeholder.Title = "Social Features"; + placeholder.SetMessage("Connect with friends and share your progress. Launching soon."); + scrollSnap.Add(placeholder); + + // Page 3 — another placeholder + var placeholder2 = new ComingSoonMessage(); + placeholder2.Title = "Challenges"; + placeholder2.SetMessage("Weekly challenges and leaderboards are on their way."); + scrollSnap.Add(placeholder2); + } +} +``` + +### Dynamic Title Update + +```csharp +// Swap placeholder text based on user role +if (user.IsPremium) +{ + _comingSoon.Title = "Advanced Analytics"; + _comingSoon.SetMessage("Your detailed stats dashboard is being prepared."); +} +else +{ + _comingSoon.Title = "Premium Feature"; + _comingSoon.SetMessage("Upgrade to unlock this section."); +} +``` diff --git a/Documentation~/Controls/GrayscaleImage.md b/Documentation~/Controls/GrayscaleImage.md new file mode 100644 index 0000000..4df031e --- /dev/null +++ b/Documentation~/Controls/GrayscaleImage.md @@ -0,0 +1,92 @@ +# GrayscaleImage + +## Summary + +`GrayscaleImage` is an `ImmediateModeElement` that renders a sprite or texture using `Graphics.DrawTexture` and supports toggling a greyscale effect via a material property. The greyscale effect requires a custom `Material` that exposes `_MainTex` and `_GreyscaleEnabled` shader properties; without a compatible material the image renders in full color only. + +Typical use cases: + +- Profile or media images that switch to greyscale when disabled or locked +- Toggling greyscale on achievement/badge imagery for locked states +- Any image that needs a shader-driven color/greyscale toggle without a separate texture asset + +## Properties + +| Name | Description | Options | +| --- | --- | --- | +| `SpriteProperty` | The `Sprite` to render. Mutually exclusive with `TextureProperty`. | `Sprite` | +| `TextureProperty` | The `Texture` to render. Mutually exclusive with `SpriteProperty`. | `Texture` | +| `scaleMode` | How the image is scaled within its bounds. | `ScaleMode` | +| `Material` | The material used for rendering. Must expose `_MainTex` and `_GreyscaleEnabled` for the greyscale feature. | `Material` | +| `GreyscaleEnabled` | Gets or sets whether the greyscale shader effect is active. Only functional when a compatible material is assigned. | `bool` | +| `MainTextureProperty` | The shader property name for the main texture. | `string` (default `"_MainTex"`) | +| `GreyscaleToggleProperty` | The shader property name for the greyscale toggle. | `string` (default `"_GreyscaleEnabled"`) | + +### Constants + +| Name | Value | Description | +| --- | --- | --- | +| `DefaultMainTextureProperty` | `"_MainTex"` | Default shader property name for the main texture. | +| `DefaultGreyscaleToggleProperty` | `"_GreyscaleEnabled"` | Default shader property name for the greyscale toggle. | + +## USS Classes + +| Class | Description | +| --- | --- | +| `grayscaleImage` | Root element. Dimensions should be set via USS or inline style; the element uses its resolved layout rect for `Graphics.DrawTexture`. | + +## Events + +This control does not emit events. Repaint is triggered automatically by the immediate-mode element lifecycle. + +## Public Methods + +This control exposes its API entirely through properties. No additional public methods are defined beyond those inherited from `ImmediateModeElement`. + +## Using the Control + +### Toggle Greyscale on a Locked Achievement + +```csharp +using UnityEngine; +using UnityEngine.UIElements; +using UnityUIToolkit.Extensions; + +public class AchievementBadgeController : MonoBehaviour +{ + [SerializeField] private UIDocument _document; + [SerializeField] private Sprite _badgeSprite; + [SerializeField] private Material _greyscaleMaterial; // shader with _MainTex + _GreyscaleEnabled + + private GrayscaleImage _badgeImage; + + private void OnEnable() + { + var root = _document.rootVisualElement; + + _badgeImage = new GrayscaleImage(); + _badgeImage.style.width = 80; + _badgeImage.style.height = 80; + _badgeImage.SpriteProperty = _badgeSprite; + _badgeImage.Material = _greyscaleMaterial; + _badgeImage.scaleMode = ScaleMode.ScaleToFit; + + root.Q("badgeContainer").Add(_badgeImage); + } + + public void SetLocked(bool locked) + { + _badgeImage.GreyscaleEnabled = locked; + } +} +``` + +### Texture-Based Rendering with Custom Property Names + +```csharp +// If your shader uses different property names: +_badgeImage.MainTextureProperty = "_BaseMap"; +_badgeImage.GreyscaleToggleProperty = "_UseGreyscale"; +_badgeImage.TextureProperty = _photoTexture; +_badgeImage.GreyscaleEnabled = true; +``` diff --git a/Documentation~/Controls/IconLabelButton.md b/Documentation~/Controls/IconLabelButton.md new file mode 100644 index 0000000..221d312 --- /dev/null +++ b/Documentation~/Controls/IconLabelButton.md @@ -0,0 +1,99 @@ +# IconLabelButton + +## Summary + +`IconLabelButton` is a full-width row button that pairs a 24 × 24 px icon on the left with a text label. It provides hover and pressed state modifier classes for visual feedback without custom USS. + +Typical use cases: + +- Menu and navigation row items +- Action list rows (share, delete, report, etc.) +- Settings list entries with a leading icon + +## Properties + +| Name | Description | Options | +| --- | --- | --- | +| `Text` | Gets or sets the button label text. | `string` | + +## USS Classes + +| Class | Description | +| --- | --- | +| `iconLabelButton` | Root element. Full-width flex row. | +| `iconLabelButton__button` | Inner button element that wraps icon and label. | +| `iconLabelButton__icon` | Icon element. Fixed at 24 × 24 px. | +| `iconLabelButton__label` | Text label element next to the icon. | +| `iconLabelButton--hover` | Modifier applied on pointer-enter. | +| `iconLabelButton--pressed` | Modifier applied while pointer is held down. | + +## Events + +| Name | Description | Arguments | +| --- | --- | --- | +| `Clicked` | Fired when the button is tapped or clicked. | none | + +## Public Methods + +No public methods beyond property access. Use the `Text` property and USS to configure the control. + +## Using the Control + +### Navigation Menu + +```csharp +using UnityEngine; +using UnityEngine.UIElements; +using UnityUIToolkit.Extensions; + +public class SideMenuController : MonoBehaviour +{ + [SerializeField] private UIDocument _document; + [SerializeField] private Texture2D _homeIcon; + [SerializeField] private Texture2D _profileIcon; + [SerializeField] private Texture2D _settingsIcon; + + private void OnEnable() + { + var root = _document.rootVisualElement; + var menu = root.Q("sideMenu"); + + menu.Add(CreateMenuRow("Home", _homeIcon, () => NavigateTo("home"))); + menu.Add(CreateMenuRow("Profile", _profileIcon, () => NavigateTo("profile"))); + menu.Add(CreateMenuRow("Settings", _settingsIcon, () => NavigateTo("settings"))); + } + + private IconLabelButton CreateMenuRow(string label, Texture2D icon, System.Action onClicked) + { + var btn = new IconLabelButton(); + btn.Text = label; + + // Query by class name (first arg is element name — pass null; second is class name) + var iconEl = btn.Q(null, IconLabelButton.IconClass); + if (iconEl != null) + iconEl.style.backgroundImage = new StyleBackground(icon); + + btn.Clicked += onClicked; + return btn; + } + + private void NavigateTo(string screen) + { + Debug.Log($"Navigating to: {screen}"); + } +} +``` + +### USS Customization + +Override hover and pressed states in your project USS: + +```uss +.iconLabelButton--hover { + background-color: rgba(255, 255, 255, 0.06); +} + +.iconLabelButton--pressed { + background-color: rgba(255, 255, 255, 0.12); +} +``` diff --git a/Documentation~/Controls/LoadingIcon.md b/Documentation~/Controls/LoadingIcon.md new file mode 100644 index 0000000..532af68 --- /dev/null +++ b/Documentation~/Controls/LoadingIcon.md @@ -0,0 +1,98 @@ +# LoadingIcon + +## Summary + +`LoadingIcon` is a continuously rotating image element used to indicate background work. Rotation is driven by a scheduled callback that fires every 16 ms. An optional `blockInteraction` flag captures pointer events so the user cannot interact with elements beneath the spinner while it is active. + +Typical use cases: + +- Async operation feedback (API calls, data loading) +- Image upload or file transfer progress indicator +- Form submission spinner overlay + +## Properties + +This control is configured through method calls. The visibility and animation state are reflected in modifier classes. + +## USS Classes + +| Class | Description | +| --- | --- | +| `loadingIcon` | Root element. | +| `loadingIcon__image` | The rotating image. Fixed at 40 × 40 px. | +| `loadingIcon--animating` | Modifier applied while rotation is active. | +| `loadingIcon--visible` | Modifier applied while the icon is shown. Pair with USS to control opacity or display. | + +## Events + +This control does not emit events. + +## Public Methods + +| Signature | Description | +| --- | --- | +| `SetIcon(Texture2D texture)` | Sets the texture used for the spinning image. | +| `PlayLoading(float customSpeed = 1f, bool blockInteraction = false)` | Starts the rotation animation. `customSpeed` is the duration of one full 360° rotation in seconds; lower values spin faster. When `blockInteraction` is `true` the control captures all pointer events. | +| `StopLoading()` | Stops the rotation, releases pointer capture, and removes the `--animating` and `--visible` modifiers. | + +## Using the Control + +### Simple Loading Overlay + +```csharp +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.UIElements; +using UnityUIToolkit.Extensions; + +public class UploadController : MonoBehaviour +{ + [SerializeField] private UIDocument _document; + [SerializeField] private Texture2D _spinnerTexture; + + private LoadingIcon _spinner; + + private void OnEnable() + { + var root = _document.rootVisualElement; + + _spinner = new LoadingIcon(); + _spinner.SetIcon(_spinnerTexture); + + root.Q("overlayContainer").Add(_spinner); + + root.Q