├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── gradle_build.yml │ └── gradle_publish.yml ├── .gitignore ├── CHANGELOG.md ├── HEADER ├── LICENSE ├── README.md ├── build.gradle.kts ├── build_logic ├── build.gradle.kts ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ ├── spruceui-common.gradle.kts │ └── spruceui │ ├── Constants.kt │ └── task │ └── XplatTransformJar.kt ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── moj_xplat └── build.gradle.kts ├── settings.gradle.kts ├── src ├── main │ ├── java │ │ └── dev │ │ │ └── lambdaurora │ │ │ └── spruceui │ │ │ ├── Position.java │ │ │ ├── SprucePositioned.java │ │ │ ├── SpruceTexts.java │ │ │ ├── SpruceTextures.java │ │ │ ├── SpruceUI.java │ │ │ ├── Tooltip.java │ │ │ ├── Tooltipable.java │ │ │ ├── background │ │ │ ├── Background.java │ │ │ ├── DirtTexturedBackground.java │ │ │ ├── EmptyBackground.java │ │ │ ├── MenuBackground.java │ │ │ └── SimpleColorBackground.java │ │ │ ├── border │ │ │ ├── Border.java │ │ │ ├── EmptyBorder.java │ │ │ ├── MenuBorder.java │ │ │ ├── SimpleBorder.java │ │ │ └── TexturedBorder.java │ │ │ ├── event │ │ │ ├── EventUtil.java │ │ │ ├── OpenScreenCallback.java │ │ │ └── ResolutionChangeCallback.java │ │ │ ├── mixin │ │ │ └── MinecraftClientMixin.java │ │ │ ├── navigation │ │ │ ├── NavigationDirection.java │ │ │ └── NavigationUtils.java │ │ │ ├── option │ │ │ ├── SpruceBooleanOption.java │ │ │ ├── SpruceCheckboxBooleanOption.java │ │ │ ├── SpruceCyclingOption.java │ │ │ ├── SpruceDoubleInputOption.java │ │ │ ├── SpruceDoubleOption.java │ │ │ ├── SpruceFloatInputOption.java │ │ │ ├── SpruceIntegerInputOption.java │ │ │ ├── SpruceOption.java │ │ │ ├── SpruceSeparatorOption.java │ │ │ ├── SpruceSimpleActionOption.java │ │ │ ├── SpruceStringOption.java │ │ │ └── SpruceToggleBooleanOption.java │ │ │ ├── screen │ │ │ ├── SpruceHandledScreen.java │ │ │ └── SpruceScreen.java │ │ │ ├── util │ │ │ ├── ColorUtil.java │ │ │ ├── Identifiable.java │ │ │ ├── MultilineText.java │ │ │ ├── Nameable.java │ │ │ ├── RenderUtil.java │ │ │ └── SpruceUtil.java │ │ │ ├── widget │ │ │ ├── AbstractSpruceBooleanButtonWidget.java │ │ │ ├── AbstractSpruceButtonWidget.java │ │ │ ├── AbstractSpruceIconButtonWidget.java │ │ │ ├── AbstractSprucePressableButtonWidget.java │ │ │ ├── AbstractSpruceWidget.java │ │ │ ├── SpruceButtonWidget.java │ │ │ ├── SpruceCheckboxWidget.java │ │ │ ├── SpruceElement.java │ │ │ ├── SpruceIconButtonWidget.java │ │ │ ├── SpruceLabelWidget.java │ │ │ ├── SpruceSeparatorWidget.java │ │ │ ├── SpruceSliderWidget.java │ │ │ ├── SpruceTexturedButtonWidget.java │ │ │ ├── SpruceToggleSwitch.java │ │ │ ├── SpruceWidget.java │ │ │ ├── SpruceWidgetWithBorder.java │ │ │ ├── WithBackground.java │ │ │ ├── WithBorder.java │ │ │ ├── container │ │ │ │ ├── AbstractSpruceParentWidget.java │ │ │ │ ├── SpruceContainerWidget.java │ │ │ │ ├── SpruceEntryListWidget.java │ │ │ │ ├── SpruceOptionListWidget.java │ │ │ │ ├── SpruceParentWidget.java │ │ │ │ └── tabbed │ │ │ │ │ └── SpruceTabbedWidget.java │ │ │ ├── option │ │ │ │ └── SpruceOptionSliderWidget.java │ │ │ └── text │ │ │ │ ├── AbstractSpruceTextInputWidget.java │ │ │ │ ├── SpruceNamedTextFieldWidget.java │ │ │ │ ├── SpruceTextAreaWidget.java │ │ │ │ ├── SpruceTextFieldWidget.java │ │ │ │ └── SpruceTextFieldWidgetBuilder.java │ │ │ └── wrapper │ │ │ └── VanillaButtonWrapper.java │ └── resources │ │ ├── assets │ │ └── spruceui │ │ │ ├── icon.png │ │ │ ├── lang │ │ │ ├── de_de.json │ │ │ ├── en_ud.json │ │ │ ├── en_us.json │ │ │ ├── es_es.json │ │ │ ├── es_mx.json │ │ │ ├── et_ee.json │ │ │ ├── fr_ca.json │ │ │ ├── fr_fr.json │ │ │ ├── hi_in.json │ │ │ ├── it_it.json │ │ │ ├── pl_pl.json │ │ │ ├── ru_ru.json │ │ │ ├── tt_ru.json │ │ │ ├── zh_cn.json │ │ │ └── zh_tw.json │ │ │ ├── powertaters │ │ │ └── liltaterreloaded │ │ │ │ └── lil_beanos.json │ │ │ └── textures │ │ │ ├── gui │ │ │ ├── bottom_right_border_separator.png │ │ │ ├── inworld_bottom_right_border_separator.png │ │ │ ├── inworld_right_border_separator.png │ │ │ ├── inworld_top_right_border_separator.png │ │ │ ├── legacy_options_background.png │ │ │ ├── right_border_separator.png │ │ │ ├── sprites │ │ │ │ ├── border │ │ │ │ │ ├── simple.png │ │ │ │ │ ├── simple.png.mcmeta │ │ │ │ │ ├── simple_highlighted.png │ │ │ │ │ └── simple_highlighted.png.mcmeta │ │ │ │ └── widget │ │ │ │ │ ├── checkbox │ │ │ │ │ ├── checked.png │ │ │ │ │ └── crossed.png │ │ │ │ │ └── toggle_switch │ │ │ │ │ ├── background.png │ │ │ │ │ ├── background_highlighted.png │ │ │ │ │ ├── off.png │ │ │ │ │ ├── off_highlighted.png │ │ │ │ │ ├── on.png │ │ │ │ │ └── on_highlighted.png │ │ │ └── top_right_border_separator.png │ │ │ └── tater │ │ │ ├── irritated_beanos.png │ │ │ └── lil_beanos.png │ │ ├── high_contrast │ │ └── assets │ │ │ └── spruceui │ │ │ └── textures │ │ │ └── gui │ │ │ ├── bottom_right_border_separator.png │ │ │ ├── inworld_bottom_right_border_separator.png │ │ │ ├── inworld_right_border_separator.png │ │ │ ├── inworld_top_right_border_separator.png │ │ │ ├── right_border_separator.png │ │ │ ├── sprites │ │ │ └── widget │ │ │ │ └── toggle_switch │ │ │ │ ├── background.png │ │ │ │ ├── background_highlighted.png │ │ │ │ ├── off.png │ │ │ │ ├── off_highlighted.png │ │ │ │ ├── on.png │ │ │ │ └── on_highlighted.png │ │ │ └── top_right_border_separator.png │ │ ├── spruceui.accesswidener │ │ └── spruceui.mixins.json └── testmod │ ├── java │ └── dev │ │ └── lambdaurora │ │ └── spruceui │ │ └── test │ │ ├── SpruceUITest.java │ │ ├── TestEnum.java │ │ ├── gui │ │ ├── SpruceMainMenuScreen.java │ │ ├── SpruceOptionScreen.java │ │ ├── SpruceTabbedTestScreen.java │ │ └── SpruceTextAreaScreen.java │ │ └── mixin │ │ └── TitleScreenMixin.java │ └── resources │ ├── assets │ └── spruceui_test │ │ ├── icon.png │ │ └── lang │ │ ├── en_ud.json │ │ ├── en_us.json │ │ └── hi_in.json │ ├── fabric.mod.json │ └── spruceui.test.mixins.json └── textures └── gui └── checkbox.kra /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Linux] 28 | - Minecraft [e.g. 1.14.4] 29 | - Fabric [e.g. fabric 0.7.2+build.174] 30 | - Mods [e.g. aurora_keystrokes v1.0.0, modmenu v1.7.15] 31 | - Version [e.g. 1.0.0] 32 | - Branch [e.g. dev] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/gradle_build.yml: -------------------------------------------------------------------------------- 1 | name: Gradle Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | build: 13 | strategy: 14 | matrix: 15 | java: [ 21 ] 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: "Checkout" 19 | uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 0 22 | - name: "Set up Java" 23 | uses: actions/setup-java@v4 24 | with: 25 | distribution: "temurin" 26 | java-version: ${{ matrix.java }} 27 | - name: "Set up Gradle" 28 | uses: gradle/actions/setup-gradle@v3 29 | with: 30 | cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/1.') && !startsWith(github.ref, 'refs/heads/dev/') && !startsWith(github.ref, 'refs/tags/v') }} 31 | 32 | - name: "Gradle Build and Test" 33 | run: ./gradlew build --stacktrace --parallel 34 | 35 | - name: "Upload artifacts" 36 | uses: actions/upload-artifact@v4 37 | with: 38 | name: Artifacts_j${{ matrix.java }} 39 | path: ./build/libs/ 40 | -------------------------------------------------------------------------------- /.github/workflows/gradle_publish.yml: -------------------------------------------------------------------------------- 1 | name: Gradle Package 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: "Checkout" 13 | uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | - name: "Set up Java" 17 | uses: actions/setup-java@v4 18 | with: 19 | distribution: "temurin" 20 | java-version: 21 21 | - name: "Set up Gradle" 22 | uses: gradle/actions/setup-gradle@v3 23 | 24 | - name: "Gradle Build" 25 | run: ./gradlew build --stacktrace --parallel 26 | 27 | - name: "Upload artifacts" 28 | uses: actions/upload-artifact@v4 29 | with: 30 | name: Artifacts 31 | path: ./build/libs/ 32 | 33 | # The USERNAME and PASSWORD need to correspond to the credentials environment variables used in 34 | # the publishing section of your build.gradle 35 | - name: "Publish to GitHub Packages and other Mavens" 36 | run: ./gradlew publish --stacktrace 37 | env: 38 | BRANCH_NAME: ${{ github.ref }} 39 | RUN_COUNT: ${{ github.run_number }} 40 | REPO_NAME: ${{ github.repository }} 41 | USERNAME: ${{ github.actor }} 42 | TOKEN: ${{ secrets.GITHUB_TOKEN }} 43 | SPRUCEUI_MAVEN: ${{ secrets.MAVEN_URL }} 44 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 45 | MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # LambdAurora's ignore file 3 | # 4 | # v0.23 5 | 6 | # JetBrains 7 | .idea/ 8 | *.iml 9 | *.ipr 10 | *.iws 11 | ## Intellij IDEA 12 | out/ 13 | ## CLion 14 | cmake-build-debug*/ 15 | cmake-build-release*/ 16 | ## Eclipse 17 | eclipse 18 | *.launch 19 | .settings 20 | .metadata 21 | .classpath 22 | .project 23 | ## Visual Studio 24 | .vs/ 25 | CMakeSettings.json 26 | 27 | # Build system 28 | ## Cargo 29 | Cargo.lock 30 | ## CMake 31 | CMakeCache.txt 32 | CMakeLists.txt.user 33 | CMakeFiles/ 34 | ## QMake 35 | .qmake.stash 36 | ## Gradle 37 | .gradle/ 38 | ## Node.JS 39 | node_modules/ 40 | ## PHP composer 41 | vendor/ 42 | 43 | # Editors 44 | ## VSCode 45 | .vscode/ 46 | 47 | # Logging 48 | logs/ 49 | 50 | # Languages 51 | ## Java 52 | classes/ 53 | ## Python 54 | __pycache__/ 55 | venv/ 56 | ## Rust 57 | **/*.rs.bk 58 | 59 | # OS 60 | ## Windows 61 | desktop.ini 62 | ## MacOS 63 | .DS_Store 64 | 65 | # File types 66 | *.dll 67 | *.db 68 | *.tar.?z 69 | 70 | # Asset files automatic backup 71 | .*.png-autosave.kra 72 | *.png~ 73 | 74 | # Compilation artifacts/Binaries 75 | *.o 76 | *.so 77 | *.dylib 78 | *.lib 79 | lib*.a 80 | 81 | # Common 82 | bin/ 83 | build/ 84 | dist/ 85 | lib/ 86 | !/lib/ 87 | !src/lib/ 88 | !src/**/lib/ 89 | obj/ 90 | run/ 91 | target/ 92 | /local.properties 93 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # SpruceUI Changelog 2 | 3 | ### 3.2.0 4 | 5 | - Removed some usage of lambdajcommon. 6 | 7 | #### 3.2.1 8 | 9 | - Switched calls to OpenGL scissor to use RenderSystem instead in ScissorManager. 10 | - Improved slider release detection. 11 | - Removed all usage of lambdajcommon, its inclusion is now deprecated. 12 | 13 | ### 3.3.0 14 | 15 | - Added ability to dynamically remove entries for a `SpruceTabbedWidget`. 16 | - Added a variant of `SpruceScreen` for `HandledScreen`s. 17 | - Adjusted the consumers of `Tooltip#queueFor` to avoid boxing. 18 | - Removed bunch of useless `@NotNull` annotations. 19 | 20 | #### 3.3.1 21 | 22 | - Added ability for `SpruceEntryListWidget` children to override scroll behaviour ([#23](https://github.com/LambdAurora/SpruceUI/pull/23)). 23 | - Removed inclusion of lambdajcommon. 24 | 25 | #### 3.3.2 26 | 27 | - Removed exposed ModMenu in POM publication. 28 | 29 | #### 3.3.3 30 | 31 | - Updated to 1.18.2, fix transitiveness issues. 32 | 33 | ## 4.0.0 34 | 35 | - Updated to Minecraft 1.19. 36 | 37 | ### 4.1.0 38 | 39 | - Updated libraries. 40 | - Couple minor bug fixes to improve UX ([#31](https://github.com/LambdAurora/SpruceUI/pull/31)). 41 | - Updated russian translations ([#30](https://github.com/LambdAurora/SpruceUI/pull/30)). 42 | - Added Traditional Chinese translations ([#34](https://github.com/LambdAurora/SpruceUI/pull/34)). 43 | 44 | ### 4.2.0 45 | 46 | - Updated to Minecraft 1.19.4. 47 | - Added High Contrast textures for SpruceUI widgets. 48 | 49 | ## 5.0.0 50 | 51 | - Updated to Minecraft 1.20. 52 | - Switched to new versioning scheme where each major Minecraft version results in a library major bump. 53 | - Use the new `GuiGraphics` Minecraft class which will cause heavy breakage. 54 | 55 | ### 5.0.1 56 | 57 | - Fixed some shader color leaking in the boolean checkbox option. 58 | 59 | ### 5.0.2 60 | 61 | - Fixed wrong background rendering in SpruceScreen. 62 | 63 | ### 5.0.3 64 | 65 | - Updated to Minecraft 1.20.2 ([#46](https://github.com/LambdAurora/SpruceUI/pull/46)). 66 | - Added Tatar translations ([#44](https://github.com/LambdAurora/SpruceUI/pull/44)). 67 | 68 | ## 5.1.0 69 | 70 | - Updated to Minecraft 1.20.6 ([#51](https://github.com/LambdAurora/SpruceUI/pull/51)). 71 | - Added `MenuBackground` and `MenuBorder` to adapt to Minecraft's new GUI design. 72 | 73 | ## 6.0.0 74 | 75 | - Updated to Minecraft 1.21.2. 76 | - Added `TexturedBorder` for textured borders. 77 | - Updated widget textures to match the new sprite system. 78 | - Removed `ScissorManager` in favor of `GuiGraphics` scissor handling. 79 | 80 | ### 6.0.1 81 | 82 | - Fixed bad Minecraft version range. 83 | 84 | ## 6.1.0 85 | 86 | - Added a way to use `SpruceToggleBooleanOption` without text. 87 | - Added placeholder to `SpruceTextFieldWidget` and `SpruceTextAreaWidget`. 88 | - Improved `SpruceTabbedWidget` construction and management. 89 | - Fixed change listener not triggering when deleting a selection in `SpruceTextFieldWidget` and `SpruceTextAreaWidget`. 90 | 91 | ## 6.2.0 92 | 93 | - Added upside-down English translations ([#57](https://github.com/LambdAurora/SpruceUI/pull/57)). 94 | - Made button texts scrolling like in modern Minecraft buttons. 95 | - Improved handling of long titles in `SpruceSeparatorWidget`. 96 | - Improved scrollbar refocus when entries change in `SpruceEntryListWidget`. 97 | 98 | ### 6.2.1 99 | 100 | - Added Hindi translations ([#59](https://github.com/LambdAurora/SpruceUI/pull/59)). 101 | 102 | ### 6.2.2 103 | 104 | - Fixed a severe silent build error. 105 | 106 | ## 7.0.0 107 | 108 | - Updated to Minecraft 1.21.5. 109 | - Removed HUD-related APIs in favor of Fabric API's own HUD APIs. 110 | - List widgets now use the proper scrollbar sprites. 111 | 112 | ### 7.0.1 113 | 114 | - Added support for NeoForge. 115 | 116 | ### 7.0.2 117 | 118 | - Fixed Minecraft version dependency constraint and other issues in FMJ. 119 | -------------------------------------------------------------------------------- /HEADER: -------------------------------------------------------------------------------- 1 | Copyright © ${CREATION_YEAR} LambdAurora 2 | 3 | This file is part of SpruceUI. 4 | 5 | Licensed under the MIT license. For more information, 6 | see the LICENSE file. 7 | 8 | #year_selection file 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2020-2023 LambdAurora 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SpruceUI 2 | 3 | ![Java 21](https://img.shields.io/badge/language-Java%2021-9115ff.svg?style=flat-square) 4 | [![GitHub license](https://img.shields.io/github/license/LambdAurora/SpruceUI?style=flat-square)](https://raw.githubusercontent.com/LambdAurora/SpruceUI/master/LICENSE) 5 | ![Environment: Client](https://img.shields.io/badge/environment-client-1976d2?style=flat-square) 6 | ![Mod loader: Fabric](https://img.shields.io/badge/modloader-Fabric-1976d2?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAFHGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDIgNzkuMTYwOTI0LCAyMDE3LzA3LzEzLTAxOjA2OjM5ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE4LTEyLTE2VDE2OjU0OjE3LTA4OjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0wNy0yOFQyMToxNzo0OC0wNzowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wNy0yOFQyMToxNzo0OC0wNzowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowZWRiMWMyYy1mZjhjLWU0NDEtOTMxZi00OTVkNGYxNGM3NjAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MGVkYjFjMmMtZmY4Yy1lNDQxLTkzMWYtNDk1ZDRmMTRjNzYwIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6MGVkYjFjMmMtZmY4Yy1lNDQxLTkzMWYtNDk1ZDRmMTRjNzYwIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDowZWRiMWMyYy1mZjhjLWU0NDEtOTMxZi00OTVkNGYxNGM3NjAiIHN0RXZ0OndoZW49IjIwMTgtMTItMTZUMTY6NTQ6MTctMDg6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE4IChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4/HiGMAAAAtUlEQVRYw+XXrQqAMBQF4D2P2eBL+QIG8RnEJFaNBjEum+0+zMQLtwwv+wV3ZzhhMDgfJ0wUSinxZUQWgKos1JP/AbD4OneIDyQPwCFniA+EJ4CaXm4TxAXCC0BNHgLhAdAnx9hC8PwGSRtAFVMQjF7cNTWED8B1cgwW20yfJgAvrssAsZ1cB3g/xckAxr6FmCDU5N6f488BrpCQ4rQBJkiMYh4ACmLzwOQF0CExinkCsvw7vgGikl+OotaKRwAAAABJRU5ErkJggg==) 7 | 8 | A Minecraft mod API which adds some GUI utilities. 9 | 10 | ## Build 11 | 12 | Just do `./gradlew build` and everything should build just fine! 13 | 14 | To test SpruceUI, you can run the testmod with `./gradlew runTestmodClient`. 15 | 16 | ## Use inside a mod 17 | 18 | You can look at the [SpruceUI test mod](https://github.com/LambdAurora/SpruceUI/tree/1.19.4/src/testmod) for examples of use. 19 | 20 | ### Import inside a project 21 | 22 | Add this to your `build.gradle` in addition of the base Fabric mod `build.gradle`: 23 | 24 | ```groovy 25 | repositories { 26 | maven { 27 | name = "Gegy" 28 | url = uri("https://maven.gegy.dev") 29 | } 30 | } 31 | 32 | dependencies { 33 | /* Fabric definitions */ 34 | 35 | include(modImplementation("dev.lambdaurora:spruceui:${project.spruceui_version}")) 36 | } 37 | ``` 38 | 39 | And this to your `gradle.properties`: 40 | 41 | ```properties 42 | spruceui_version=7.0.1+1.21.5 43 | ``` 44 | 45 | It will JAR-in-JAR SpruceUI so users of your mod don't need to download it separately! 46 | 47 | #### For NeoForge 48 | 49 | Defining the dependency on NeoForge is slightly different: 50 | 51 | ```kotlin 52 | dependencies { 53 | val spruceui = implementation("dev.lambdaurora:spruceui:${project.spruceui_version}") { 54 | capabilities { 55 | requireCapability("dev.lambdaurora:spruceui-mojmap") 56 | } 57 | } 58 | 59 | jarJar(spruceui) { 60 | jarJar.pin(it, "[7.0.1,8.0.0)") 61 | } 62 | } 63 | ``` 64 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import net.fabricmc.loom.task.RemapJarTask 2 | import spruceui.Constants 3 | 4 | plugins { 5 | id("spruceui-common") 6 | id("dev.yumi.gradle.licenser").version("2.1.+") 7 | `java-library` 8 | `maven-publish` 9 | } 10 | 11 | base.archivesName.set(Constants.NAMESPACE) 12 | 13 | val fabricModules = setOf( 14 | "fabric-api-base", 15 | "fabric-lifecycle-events-v1", 16 | "fabric-rendering-v1", 17 | "fabric-resource-loader-v0", 18 | "fabric-screen-api-v1", 19 | "fabric-key-binding-api-v1" 20 | ) 21 | 22 | val testmod: SourceSet by sourceSets.creating { 23 | this.compileClasspath += sourceSets.main.get().compileClasspath 24 | this.runtimeClasspath += sourceSets.main.get().runtimeClasspath 25 | } 26 | 27 | lambdamcdev.manifests { 28 | fmj { 29 | withName(Constants.PRETTY_NAME) 30 | withDescription(Constants.DESCRIPTION) 31 | withAuthors(Constants.AUTHORS) 32 | withContact { 33 | it.withHomepage(Constants.PROJECT_LINK) 34 | .withSources(Constants.SOURCES_LINK) 35 | .withIssues(Constants.ISSUES_LINK) 36 | } 37 | withLicense(Constants.LICENSE) 38 | withIcon("assets/${Constants.NAMESPACE}/icon.png") 39 | withEnvironment("client") 40 | withDepend("fabricloader", ">=${libs.versions.fabric.loader.get()}") 41 | withDepend("minecraft", "~1.21.5- <1.21.6-") 42 | withDepend("fabric-resource-loader-v0", ">=0.4.7") 43 | withDepend("java", ">=${Constants.JAVA_VERSION}") 44 | withAccessWidener("spruceui.accesswidener") 45 | withMixins("spruceui.mixins.json") 46 | 47 | withModMenu { 48 | it.withBadges("library") 49 | .withDiscord("https://discord.lambdaurora.dev/") 50 | .withLink("modmenu.bluesky", "https://bsky.app/profile/lambdaurora.dev") 51 | } 52 | } 53 | } 54 | 55 | repositories { 56 | mavenLocal() 57 | maven { 58 | name = "TerraformersMC" 59 | url = uri("https://maven.terraformersmc.com/releases") 60 | } 61 | maven { 62 | name = "Gegy" 63 | url = uri("https://maven.gegy.dev/releases/") 64 | } 65 | } 66 | 67 | dependencies { 68 | @Suppress("UnstableApiUsage") 69 | mappings(lambdamcdev.layered { 70 | officialMojangMappings() 71 | // Parchment is currently broken when used with the hacked mojmap layer due to remapping shenanigans. 72 | //parchment("org.parchmentmc.data:parchment-${mcVersion}:${project.property("parchment_mappings")}@zip") 73 | mappings("dev.lambdaurora:yalmm:${Constants.mcVersion()}+build.${libs.versions.mappings.yalmm.get()}") 74 | }) 75 | modImplementation(libs.fabric.loader) 76 | 77 | fabricModules.stream().map { fabricApi.module(it, libs.versions.fabric.api.get()) }.forEach { 78 | modImplementation(it) 79 | } 80 | 81 | modLocalRuntime(libs.modmenu) { 82 | isTransitive = false 83 | } 84 | 85 | "testmodImplementation"(sourceSets.main.get().output) 86 | } 87 | 88 | val mojmap by sourceSets.creating {} 89 | 90 | java { 91 | registerFeature("mojmap") { 92 | usingSourceSet(mojmap) 93 | withSourcesJar() 94 | } 95 | } 96 | 97 | tasks.withType().configureEach { 98 | options.isDeprecation = true 99 | options.isIncremental = true 100 | } 101 | 102 | tasks.jar { 103 | inputs.property("archivesName", base.archivesName) 104 | 105 | from("LICENSE") { 106 | rename { "${it}_${inputs.properties["archivesName"]}" } 107 | } 108 | } 109 | 110 | loom { 111 | accessWidenerPath = file("src/main/resources/spruceui.accesswidener") 112 | runs { 113 | register("testmodClient") { 114 | client() 115 | source(testmod) 116 | } 117 | } 118 | } 119 | 120 | val testmodJar = tasks.register("testmodJar") { 121 | this.group = "build" 122 | this.from(testmod.output) 123 | this.archiveClassifier = "testmod-dev" 124 | this.destinationDirectory = project.file("build/devlibs") 125 | } 126 | 127 | val remapTestmodJar = tasks.register("remapTestmodJar") { 128 | this.group = "build" 129 | this.dependsOn(testmodJar.get()) 130 | this.inputFile.set(testmodJar.get().archiveFile) 131 | this.classpath.from(testmod.compileClasspath) 132 | this.archiveClassifier = "testmod" 133 | } 134 | tasks.build.get().dependsOn(remapTestmodJar) 135 | 136 | license { 137 | rule(file("HEADER")) 138 | } 139 | 140 | // Configure the maven publication. 141 | publishing { 142 | publications { 143 | create("mavenJava") { 144 | from(components["java"]) 145 | 146 | pom { 147 | name.set(Constants.PRETTY_NAME) 148 | description.set(Constants.DESCRIPTION) 149 | } 150 | } 151 | } 152 | 153 | repositories { 154 | mavenLocal() 155 | maven { 156 | name = "BuildDirLocal" 157 | url = uri("${rootProject.layout.buildDirectory.get()}/repo") 158 | } 159 | maven { 160 | name = "GithubPackages" 161 | url = uri("https://maven.pkg.github.com/LambdAurora/SpruceUI") 162 | credentials { 163 | username = (project.findProperty("gpr.user") as? String) ?: System.getenv("USERNAME") 164 | password = (project.findProperty("gpr.key") as? String) ?: System.getenv("TOKEN") 165 | } 166 | } 167 | 168 | val spruceuiMaven = System.getenv("SPRUCEUI_MAVEN") 169 | if (spruceuiMaven != null) { 170 | maven { 171 | name = "SpruceUIMaven" 172 | url = uri(spruceuiMaven) 173 | credentials { 174 | username = (project.findProperty("gpr.user") as? String) ?: System.getenv("MAVEN_USERNAME") 175 | password = (project.findProperty("gpr.key") as? String) ?: System.getenv("MAVEN_PASSWORD") 176 | } 177 | } 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /build_logic/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 2 | 3 | plugins { 4 | `java-gradle-plugin` 5 | `kotlin-dsl` 6 | } 7 | 8 | val javaVersion = 21 9 | 10 | repositories { 11 | gradlePluginPortal() 12 | maven { 13 | name = "Fabric" 14 | url = uri("https://maven.fabricmc.net/") 15 | } 16 | maven { 17 | name = "Gegy" 18 | url = uri("https://maven.gegy.dev/releases/") 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation(libs.gradle.licenser) 24 | implementation(libs.gradle.loom) 25 | implementation(libs.gradle.lambdamcdev) 26 | 27 | // A bit of a hack you definitely should not worry about. 28 | // https://github.com/gradle/gradle/issues/15383#issuecomment-779893192 29 | implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) 30 | } 31 | 32 | java { 33 | sourceCompatibility = JavaVersion.toVersion(javaVersion) 34 | targetCompatibility = JavaVersion.toVersion(javaVersion) 35 | } 36 | 37 | kotlin { 38 | compilerOptions { 39 | jvmTarget = JvmTarget.fromTarget(javaVersion.toString()) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /build_logic/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | versionCatalogs { 3 | create("libs") { 4 | from(files("../gradle/libs.versions.toml")) 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /build_logic/src/main/kotlin/spruceui-common.gradle.kts: -------------------------------------------------------------------------------- 1 | import dev.lambdaurora.mcdev.api.McVersionLookup 2 | import spruceui.Constants 3 | import org.gradle.accessors.dm.LibrariesForLibs 4 | 5 | plugins { 6 | id("fabric-loom") 7 | id("dev.lambdaurora.mcdev") 8 | } 9 | 10 | // Seriously you should not worry about it, definitely not a hack. 11 | // https://github.com/gradle/gradle/issues/15383#issuecomment-779893192 12 | val libs = the() 13 | Constants.finalizeInit(libs) 14 | 15 | group = Constants.GROUP 16 | version = "${Constants.VERSION}+${McVersionLookup.getVersionTag(Constants.mcVersion())}" 17 | lambdamcdev.namespace = Constants.NAMESPACE 18 | 19 | repositories { 20 | mavenCentral() 21 | } 22 | 23 | dependencies { 24 | minecraft(libs.minecraft) 25 | } 26 | 27 | java { 28 | sourceCompatibility = JavaVersion.toVersion(Constants.JAVA_VERSION) 29 | targetCompatibility = JavaVersion.toVersion(Constants.JAVA_VERSION) 30 | 31 | withSourcesJar() 32 | } 33 | 34 | tasks.withType().configureEach { 35 | options.encoding = "UTF-8" 36 | 37 | options.release.set(Constants.JAVA_VERSION) 38 | } 39 | -------------------------------------------------------------------------------- /build_logic/src/main/kotlin/spruceui/Constants.kt: -------------------------------------------------------------------------------- 1 | package spruceui 2 | 3 | import org.gradle.accessors.dm.LibrariesForLibs 4 | 5 | object Constants { 6 | const val GROUP = "dev.lambdaurora" 7 | const val NAMESPACE = "spruceui" 8 | const val PRETTY_NAME = "SpruceUI" 9 | const val VERSION = "7.0.2" 10 | const val JAVA_VERSION = 21 11 | 12 | const val DESCRIPTION = "Just a Minecraft GUI library." 13 | 14 | @JvmField 15 | val AUTHORS = listOf("LambdAurora") 16 | 17 | const val PROJECT_LINK = "https://github.com/LambdAurora/SpruceUI" 18 | const val SOURCES_LINK = "$PROJECT_LINK.git" 19 | const val ISSUES_LINK = "$PROJECT_LINK/issues" 20 | const val LICENSE = "MIT" 21 | 22 | private var minecraftVersion: String? = null 23 | 24 | fun finalizeInit(libs: LibrariesForLibs) { 25 | this.minecraftVersion = libs.versions.minecraft.get() 26 | } 27 | 28 | fun mcVersion(): String { 29 | return this.minecraftVersion!! 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /build_logic/src/main/kotlin/spruceui/task/XplatTransformJar.kt: -------------------------------------------------------------------------------- 1 | package spruceui.task 2 | 3 | import dev.lambdaurora.mcdev.api.AccessWidenerToTransformer 4 | import org.gradle.api.file.RegularFileProperty 5 | import org.gradle.api.tasks.InputFile 6 | import org.gradle.jvm.tasks.Jar 7 | import java.nio.file.* 8 | import java.nio.file.attribute.BasicFileAttributes 9 | import javax.inject.Inject 10 | 11 | abstract class XplatTransformJar @Inject constructor() : Jar() { 12 | @InputFile 13 | val inputJar: RegularFileProperty = project.objects.fileProperty() 14 | 15 | override fun copy() { 16 | super.copy() 17 | 18 | val inputJar = this.inputJar.asFile.get().toPath() 19 | val outputJar = this.archiveFile.get().asFile.toPath() 20 | 21 | FileSystems.newFileSystem(outputJar).use { outFs -> 22 | this.copyJar(inputJar, outFs) 23 | this.generateAccessTransformer(outFs) 24 | } 25 | } 26 | 27 | private fun copyJar(inputJar: Path, outFs: FileSystem) { 28 | FileSystems.newFileSystem(inputJar).use { inFs -> 29 | val excludeFiles = listOf( 30 | inFs.getPath("fabric.mod.json"), 31 | inFs.getPath("spruceui.mixins.json"), 32 | inFs.getPath("spruceui-refmap.json"), 33 | ) 34 | val excludeDirs = listOf( 35 | inFs.getPath("dev/lambdaurora/spruceui/mixin"), 36 | inFs.getPath("dev/lambdaurora/spruceui/event") 37 | ) 38 | 39 | inFs.rootDirectories.forEach { root -> 40 | Files.walkFileTree(root, object : SimpleFileVisitor() { 41 | override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult { 42 | if (dir in excludeDirs) { 43 | return FileVisitResult.SKIP_SUBTREE 44 | } 45 | 46 | Files.createDirectories(outFs.getPath("$dir")) 47 | 48 | return FileVisitResult.CONTINUE 49 | } 50 | 51 | override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult { 52 | if (file in excludeFiles) { 53 | return FileVisitResult.CONTINUE 54 | } 55 | 56 | Files.copy(file, outFs.getPath("$file"), StandardCopyOption.REPLACE_EXISTING) 57 | 58 | return FileVisitResult.CONTINUE 59 | } 60 | }) 61 | } 62 | } 63 | } 64 | 65 | private fun generateAccessTransformer(fs: FileSystem) { 66 | val awPath = fs.getPath("spruceui.accesswidener") 67 | 68 | AccessWidenerToTransformer.convert( 69 | awPath, 70 | fs.getPath("META-INF/accesstransformer.cfg") 71 | ) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx2G 3 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | minecraft = "1.21.5" 3 | fabric-loader = "0.16.10" 4 | fabric-api = "0.119.5+1.21.5" 5 | mappings-yalmm = "14" 6 | modmenu="14.0.0-rc.2" 7 | 8 | # Gradle 9 | gradle-licenser = "2.1.+" 10 | gradle-loom = "1.10.+" 11 | gradle-lambdamcdev = "1.1.+" 12 | 13 | [libraries] 14 | minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" } 15 | fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" } 16 | fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" } 17 | modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" } 18 | 19 | # Gradle 20 | gradle-licenser = { module = "dev.yumi:yumi-gradle-licenser", version.ref = "gradle-licenser" } 21 | gradle-loom = { module = "net.fabricmc:fabric-loom", version.ref = "gradle-loom" } 22 | gradle-lambdamcdev = { module = "dev.lambdaurora.mcdev:dev.lambdaurora.mcdev.gradle.plugin", version.ref = "gradle-lambdamcdev" } 23 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /moj_xplat/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import dev.lambdaurora.mcdev.api.manifest.Nmt 2 | import net.fabricmc.loom.LoomGradleExtension 3 | import net.fabricmc.loom.api.mappings.layered.MappingsNamespace 4 | import net.fabricmc.loom.task.RemapJarTask 5 | import net.fabricmc.loom.task.RemapSourcesJarTask 6 | import spruceui.Constants 7 | import spruceui.task.XplatTransformJar 8 | 9 | plugins { 10 | id("spruceui-common") 11 | } 12 | 13 | base.archivesName.set(Constants.NAMESPACE + "-mojmap") 14 | 15 | lambdamcdev.manifests { 16 | nmt( 17 | rootProject.lambdamcdev.manifests.fmj().get().derive(::Nmt) 18 | .withBlurIcon(false) 19 | .withLoaderVersion("[2,)") 20 | .withDepend("minecraft", "[" + libs.versions.minecraft.get() + ",)") 21 | ) 22 | } 23 | 24 | dependencies { 25 | mappings(loom.officialMojangMappings()) 26 | } 27 | 28 | val baseProject = rootProject 29 | 30 | tasks.remapJar { 31 | val remapJar = baseProject.tasks.named("remapJar", RemapJarTask::class) 32 | dependsOn(remapJar) 33 | 34 | classpath.setFrom((loom as LoomGradleExtension).getMinecraftJarsCollection(MappingsNamespace.INTERMEDIARY)) 35 | inputFile.convention(remapJar.flatMap { it.archiveFile }) 36 | archiveClassifier = "preprocessed" 37 | sourceNamespace = "intermediary" 38 | targetNamespace = "named" 39 | } 40 | 41 | val xplatTransformJar by tasks.registering(XplatTransformJar::class) { 42 | val mainSourceSet = sourceSets.main.get() 43 | 44 | dependsOn(tasks.remapJar) 45 | dependsOn(tasks.named(mainSourceSet.processResourcesTaskName)) 46 | 47 | inputJar.set(tasks.remapJar.flatMap { it.archiveFile }) 48 | from(mainSourceSet.output.resourcesDir) 49 | } 50 | 51 | // Add the remapped JAR artifact 52 | baseProject.configurations["mojmapApiElements"].artifacts.removeIf { 53 | true 54 | } 55 | baseProject.artifacts.add("mojmapApiElements", xplatTransformJar) { 56 | classifier = "mojmap" 57 | } 58 | baseProject.configurations["mojmapRuntimeElements"].artifacts.removeIf { 59 | true 60 | } 61 | baseProject.artifacts.add("mojmapRuntimeElements", xplatTransformJar) { 62 | classifier = "mojmap" 63 | } 64 | 65 | tasks.remapSourcesJar { 66 | val remapJar = baseProject.tasks.named("remapSourcesJar", RemapSourcesJarTask::class) 67 | dependsOn(remapJar) 68 | 69 | classpath.setFrom((loom as LoomGradleExtension).getMinecraftJarsCollection(MappingsNamespace.INTERMEDIARY)) 70 | inputFile.set(remapJar.flatMap { it.archiveFile }) 71 | archiveClassifier = "preprocessed-sources" 72 | sourceNamespace = "intermediary" 73 | targetNamespace = "named" 74 | } 75 | 76 | val xplatTransformSourcesJar by tasks.registering(XplatTransformJar::class) { 77 | val mainSourceSet = sourceSets.main.get() 78 | 79 | dependsOn(tasks.remapSourcesJar) 80 | dependsOn(tasks.named(mainSourceSet.processResourcesTaskName)) 81 | 82 | inputJar.set(tasks.remapSourcesJar.flatMap { it.archiveFile }) 83 | from(mainSourceSet.output.resourcesDir) 84 | archiveClassifier = "sources" 85 | } 86 | 87 | tasks.build.configure { 88 | dependsOn(xplatTransformJar) 89 | dependsOn(xplatTransformSourcesJar) 90 | } 91 | 92 | // Add the remapped sources artifact 93 | baseProject.configurations["mojmapSourcesElements"].artifacts.removeIf { 94 | true 95 | } 96 | baseProject.artifacts.add("mojmapSourcesElements", xplatTransformSourcesJar) { 97 | classifier = "mojmap-sources" 98 | } 99 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "spruceui" 2 | 3 | pluginManagement { 4 | repositories { 5 | gradlePluginPortal() 6 | maven { 7 | name = "Fabric" 8 | url = uri("https://maven.fabricmc.net/") 9 | } 10 | maven { 11 | name = "Gegy" 12 | url = uri("https://maven.gegy.dev/releases/") 13 | } 14 | } 15 | } 16 | 17 | includeBuild("build_logic") 18 | include(":moj_xplat") 19 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/Position.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui; 11 | 12 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 13 | 14 | import java.util.Objects; 15 | 16 | /** 17 | * Represents a position. 18 | * 19 | * @author LambdAurora 20 | * @version 3.0.0 21 | * @since 1.4.0 22 | */ 23 | public final class Position implements SprucePositioned { 24 | private SprucePositioned anchor; 25 | private int x = 0; 26 | private int y = 0; 27 | 28 | protected Position(SprucePositioned anchor) { 29 | this.anchor = anchor; 30 | } 31 | 32 | public static Position of(SprucePositioned anchor, int x, int y) { 33 | return new Position(anchor).move(x, y); 34 | } 35 | 36 | public static Position of(int x, int y) { 37 | return of(origin(), x, y); 38 | } 39 | 40 | public static Position center(SpruceWidget parent, int y) { 41 | return center(parent, parent.getWidth(), y); 42 | } 43 | 44 | public static Position center(SprucePositioned anchor, int width, int y) { 45 | return of(anchor, width / 2, y); 46 | } 47 | 48 | public static Position center(int width, int y) { 49 | return of(width / 2, y); 50 | } 51 | 52 | /** 53 | * Returns the origin position. 54 | * 55 | * @return the origin position 56 | */ 57 | public static Position origin() { 58 | return new Position(new SprucePositioned() { 59 | }); 60 | } 61 | 62 | /** 63 | * Returns the anchor. 64 | * 65 | * @return the anchor 66 | */ 67 | public SprucePositioned getAnchor() { 68 | return this.anchor; 69 | } 70 | 71 | /** 72 | * Sets the anchor. 73 | * 74 | * @param anchor the anchor 75 | */ 76 | public void setAnchor(SprucePositioned anchor) { 77 | this.anchor = anchor; 78 | } 79 | 80 | @Override 81 | public int getX() { 82 | return this.anchor.getX() + this.x; 83 | } 84 | 85 | @Override 86 | public int getY() { 87 | return this.anchor.getY() + this.y; 88 | } 89 | 90 | public Position move(int x, int y) { 91 | this.setRelativeX(x); 92 | this.setRelativeY(y); 93 | return this; 94 | } 95 | 96 | /** 97 | * Gets the relative X of this position. 98 | * 99 | * @return the relative X 100 | */ 101 | public int getRelativeX() { 102 | return this.x; 103 | } 104 | 105 | /** 106 | * Sets the relative X of this position. 107 | * 108 | * @param x the relative X 109 | */ 110 | public void setRelativeX(int x) { 111 | this.x = x; 112 | } 113 | 114 | /** 115 | * Gets the relative Y of this position. 116 | * 117 | * @return the relative Y 118 | */ 119 | public int getRelativeY() { 120 | return this.y; 121 | } 122 | 123 | /** 124 | * Sets the relative Y of this position. 125 | * 126 | * @param y the relative Y 127 | */ 128 | public void setRelativeY(int y) { 129 | this.y = y; 130 | } 131 | 132 | /** 133 | * Copies the position into a new position. 134 | * 135 | * @return the copied position 136 | */ 137 | public Position copy() { 138 | return of(this.anchor, this.x, this.y); 139 | } 140 | 141 | @Override 142 | public boolean equals(Object o) { 143 | if (this == o) return true; 144 | if (o == null || getClass() != o.getClass()) return false; 145 | var position = (Position) o; 146 | return this.getX() == position.getX() && this.getY() == position.getY(); 147 | } 148 | 149 | @Override 150 | public int hashCode() { 151 | return Objects.hash(this.anchor, this.x, this.y); 152 | } 153 | 154 | @Override 155 | public String toString() { 156 | return "Position{" + 157 | "anchor=" + this.anchor + 158 | ", x=" + this.x + 159 | ", y=" + this.y + 160 | '}'; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/SprucePositioned.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui; 11 | 12 | /** 13 | * Generic interface representing an object that provides a screen position. 14 | * 15 | * @author LambdAurora 16 | * @version 3.0.0 17 | * @since 1.4.0 18 | */ 19 | public interface SprucePositioned { 20 | /** 21 | * Returns the X coordinate. 22 | * 23 | * @return the X coordinate 24 | */ 25 | default int getX() { 26 | return 0; 27 | } 28 | 29 | /** 30 | * Returns the Y coordinate. 31 | * 32 | * @return the Y coordinate 33 | */ 34 | default int getY() { 35 | return 0; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/SpruceTexts.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui; 11 | 12 | import net.minecraft.network.chat.Text; 13 | 14 | /** 15 | * Represents a text utility class. 16 | * 17 | * @author LambdAurora 18 | * @version 3.3.0 19 | * @since 1.5.7 20 | */ 21 | public final class SpruceTexts { 22 | /** 23 | * Represents the text usually present in tooltips to indicate that the link can be opened. 24 | * 25 | * @since 1.6.0 26 | */ 27 | public static final Text CHAT_LINK_OPEN = Text.translatable("chat.link.open"); 28 | 29 | /** 30 | * Represents the button text to reset a keybinding. 31 | * 32 | * @see #getNarratorControlsReset(Text) 33 | * @since 1.6.0 34 | */ 35 | public static final Text CONTROLS_RESET = Text.translatable("controls.reset"); 36 | 37 | /** 38 | * Represents the text "reset all" which is presents on a button in the controls screen. 39 | * 40 | * @since 1.6.0 41 | */ 42 | public static final Text CONTROLS_RESET_ALL = Text.translatable("controls.resetAll"); 43 | 44 | public static final Text GUI_DONE = Text.translatable("gui.done"); 45 | 46 | /** 47 | * Represents "none" as text. 48 | * 49 | * @since 2.0.0 50 | */ 51 | public static final Text GUI_NONE = Text.translatable("gui.none"); 52 | 53 | /** 54 | * Represents the unbind action as text. 55 | * 56 | * @since 2.0.0 57 | */ 58 | public static final Text GUI_UNBIND = Text.translatable("spruceui.gui.unbind"); 59 | 60 | public static final Text MENU_OPTIONS = Text.translatable("menu.options"); 61 | 62 | /** 63 | * Represents the text "not bound". 64 | * 65 | * @since 1.6.0 66 | */ 67 | public static final Text NOT_BOUND = Text.translatable("key.keyboard.unknown"); 68 | 69 | /** 70 | * Represents the option value "default" as text. 71 | */ 72 | public static final Text OPTIONS_GENERIC_DEFAULT = Text.translatable("generator.default"); 73 | 74 | /** 75 | * Represents the option value "fancy" as text. 76 | */ 77 | public static final Text OPTIONS_GENERIC_FANCY = Text.translatable("spruceui.options.generic.fancy"); 78 | 79 | /** 80 | * Represents the option value "fast" as text. 81 | */ 82 | public static final Text OPTIONS_GENERIC_FAST = Text.translatable("spruceui.options.generic.fast"); 83 | 84 | /** 85 | * Represents the option value "fastest" as text. 86 | */ 87 | public static final Text OPTIONS_GENERIC_FASTEST = Text.translatable("spruceui.options.generic.fastest"); 88 | 89 | /** 90 | * Represents the option value "simple" as text. 91 | */ 92 | public static final Text OPTIONS_GENERIC_SIMPLE = Text.translatable("spruceui.options.generic.simple"); 93 | 94 | /** 95 | * Represents the option value "on" as text. 96 | */ 97 | public static final Text OPTIONS_ON = Text.translatable("options.on"); 98 | 99 | /** 100 | * Represents the option value "off" as text. 101 | */ 102 | public static final Text OPTIONS_OFF = Text.translatable("options.off"); 103 | 104 | /** 105 | * Returns the option value whether if the option is ON or OFF. 106 | * 107 | * @param value {@code true} if the option is ON, else {@code false} 108 | * @return the option value text 109 | */ 110 | public static Text getToggleText(boolean value) { 111 | return value ? OPTIONS_ON : OPTIONS_OFF; 112 | } 113 | 114 | /** 115 | * Represents the option value "visible" as text. 116 | */ 117 | public static final Text OPTIONS_VISIBLE = Text.translatable("options.visible"); 118 | 119 | /** 120 | * Represents the option value "hidden" as text. 121 | */ 122 | public static final Text OPTIONS_HIDDEN = Text.translatable("options.hidden"); 123 | 124 | /** 125 | * Represents the "reset" text. 126 | */ 127 | public static final Text RESET_TEXT = Text.translatable("spruceui.reset"); 128 | 129 | /** 130 | * Returns the narrator text to describe the button which resets a keybinding. 131 | * 132 | * @param bindingName the binding name 133 | * @return the text 134 | * @see #CONTROLS_RESET 135 | * @since 1.6.0 136 | */ 137 | public static Text getNarratorControlsReset(Text bindingName) { 138 | return Text.translatable("narrator.controls.reset", bindingName); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/SpruceTextures.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2024 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui; 11 | 12 | import net.minecraft.client.gui.screens.Screen; 13 | import net.minecraft.resources.Identifier; 14 | 15 | /** 16 | * Contains the identifiers of various useful textures. 17 | * 18 | * @author LambdAurora 19 | * @version 6.0.0 20 | * @since 5.1.0 21 | */ 22 | public final class SpruceTextures { 23 | private SpruceTextures() { 24 | throw new UnsupportedOperationException("SpruceTextures only contain static definitions."); 25 | } 26 | 27 | /* Backgrounds */ 28 | 29 | public static final Identifier MENU_LIST_BACKGROUND = Identifier.ofDefault("textures/gui/menu_list_background.png"); 30 | public static final Identifier INWORLD_MENU_LIST_BACKGROUND = Identifier.ofDefault("textures/gui/inworld_menu_list_background.png"); 31 | 32 | /** 33 | * The dirt background texture used in pre-1.20.5 versions. 34 | */ 35 | public static final Identifier LEGACY_OPTIONS_BACKGROUND = SpruceUI.id("textures/gui/legacy_options_background.png"); 36 | 37 | /* Border */ 38 | 39 | public static final Identifier SIMPLE_BORDER_SPRITE = SpruceUI.id("border/simple"); 40 | public static final Identifier SIMPLE_HIGHLIGHTED_BORDER_SPRITE = SpruceUI.id("border/simple_highlighted"); 41 | 42 | public static final Identifier MENU_TOP_BORDER = Screen.HEADER_SEPARATOR; 43 | public static final Identifier INWORLD_MENU_TOP_BORDER = Screen.INWORLD_HEADER_SEPARATOR; 44 | public static final Identifier MENU_TOP_RIGHT_BORDER = SpruceUI.id("textures/gui/top_right_border_separator.png"); 45 | public static final Identifier INWORLD_MENU_TOP_RIGHT_BORDER = SpruceUI.id("textures/gui/inworld_top_right_border_separator.png"); 46 | public static final Identifier MENU_RIGHT_BORDER = SpruceUI.id("textures/gui/right_border_separator.png"); 47 | public static final Identifier INWORLD_MENU_RIGHT_BORDER = SpruceUI.id("textures/gui/inworld_right_border_separator.png"); 48 | public static final Identifier MENU_BOTTOM_RIGHT_BORDER = SpruceUI.id("textures/gui/bottom_right_border_separator.png"); 49 | public static final Identifier INWORLD_MENU_BOTTOM_RIGHT_BORDER = SpruceUI.id("textures/gui/inworld_bottom_right_border_separator.png"); 50 | public static final Identifier MENU_BOTTOM_BORDER = Screen.FOOTER_SEPARATOR; 51 | public static final Identifier INWORLD_MENU_BOTTOM_BORDER = Screen.INWORLD_FOOTER_SEPARATOR; 52 | 53 | /* Scroller */ 54 | public static final Identifier SCROLLER = Identifier.ofDefault("widget/scroller"); 55 | public static final Identifier SCROLLER_BACKGROUND = Identifier.ofDefault("widget/scroller_background"); 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/SpruceUI.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2024 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui; 11 | 12 | import net.minecraft.resources.Identifier; 13 | 14 | /** 15 | * Contains common constants from SpruceUI. 16 | * 17 | * @author LambdAurora 18 | * @version 6.0.0 19 | * @since 6.0.0 20 | */ 21 | public final class SpruceUI { 22 | /** 23 | * The namespace of SpruceUI, whose value is {@value}. 24 | */ 25 | public static final String NAMESPACE = "spruceui"; 26 | 27 | /** 28 | * {@return a SpruceUI identifier from the given path} 29 | * 30 | * @param path the path 31 | */ 32 | public static Identifier id(String path) { 33 | return Identifier.of(NAMESPACE, path); 34 | } 35 | 36 | private SpruceUI() { 37 | throw new UnsupportedOperationException("SpruceUi only contains static definitions."); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/Tooltip.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui; 11 | 12 | import com.google.common.collect.Queues; 13 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 14 | import net.minecraft.client.Minecraft; 15 | import net.minecraft.client.gui.GuiGraphics; 16 | import net.minecraft.client.gui.screens.inventory.tooltip.DefaultTooltipPositioner; 17 | import net.minecraft.network.chat.FormattedText; 18 | import net.minecraft.util.FormattedCharSequence; 19 | import org.jetbrains.annotations.ApiStatus; 20 | 21 | import java.util.List; 22 | import java.util.Queue; 23 | import java.util.function.IntConsumer; 24 | import java.util.function.LongConsumer; 25 | 26 | /** 27 | * Represents a tooltip. 28 | * 29 | * @author LambdAurora 30 | * @version 5.0.0 31 | * @since 1.0.0 32 | */ 33 | public class Tooltip implements SprucePositioned { 34 | private static final Queue TOOLTIPS = Queues.newConcurrentLinkedQueue(); 35 | private static boolean delayed = false; 36 | private final int x; 37 | private final int y; 38 | private final List tooltip; 39 | 40 | public Tooltip(int x, int y, String tooltip, int parentWidth) { 41 | this(x, y, FormattedText.of(tooltip), parentWidth); 42 | } 43 | 44 | public Tooltip(int x, int y, FormattedText tooltip, int parentWidth) { 45 | this(x, y, Minecraft.getInstance().font.wrapLines(tooltip, Math.max(parentWidth * 2 / 3, 200))); 46 | } 47 | 48 | public Tooltip(int x, int y, List tooltip) { 49 | this.x = x; 50 | this.y = y; 51 | this.tooltip = tooltip; 52 | } 53 | 54 | public static Tooltip create(int x, int y, String tooltip, int parentWidth) { 55 | return new Tooltip(x, y, tooltip, parentWidth); 56 | } 57 | 58 | public static Tooltip create(int x, int y, FormattedText tooltip, int parentWidth) { 59 | return new Tooltip(x, y, tooltip, parentWidth); 60 | } 61 | 62 | public static Tooltip create(int x, int y, List tooltip) { 63 | return new Tooltip(x, y, tooltip); 64 | } 65 | 66 | @Override 67 | public int getX() { 68 | return this.x; 69 | } 70 | 71 | @Override 72 | public int getY() { 73 | return this.y; 74 | } 75 | 76 | /** 77 | * Returns whether the tooltip should render or not. 78 | * 79 | * @return {@code true} if the tooltip should render, else {@code false} 80 | */ 81 | public boolean shouldRender() { 82 | return !this.tooltip.isEmpty(); 83 | } 84 | 85 | /** 86 | * Renders the tooltip. 87 | * 88 | * @param graphics The GuiGraphics instance used to render. 89 | */ 90 | public void render(GuiGraphics graphics) { 91 | graphics.drawTooltip(Minecraft.getInstance().font, this.tooltip, DefaultTooltipPositioner.INSTANCE, this.x, this.y); 92 | } 93 | 94 | /** 95 | * Queues the tooltip to render. 96 | */ 97 | public void queue() { 98 | TOOLTIPS.add(this); 99 | } 100 | 101 | /** 102 | * Queues the tooltip of the widget to render. 103 | * 104 | * @param widget the widget 105 | * @param mouseX the mouse X coordinate 106 | * @param mouseY the mouse Y coordinate 107 | * @param the type of the widget 108 | * @since 1.6.0 109 | */ 110 | public static void queueFor( 111 | T widget, 112 | int mouseX, 113 | int mouseY, 114 | int tooltipTicks, 115 | IntConsumer tooltipTicksSetter, 116 | long lastTick, 117 | LongConsumer lastTickSetter 118 | ) { 119 | if (widget.isVisible()) { 120 | widget.getTooltip().ifPresent(tooltip -> { 121 | long currentRender = System.currentTimeMillis(); 122 | if (lastTick != 0) { 123 | if (currentRender - lastTick >= 20) { 124 | tooltipTicksSetter.accept(tooltipTicks + 1); 125 | lastTickSetter.accept(currentRender); 126 | } 127 | } else lastTickSetter.accept(currentRender); 128 | 129 | if (!widget.isFocused() && !widget.isMouseHovered()) 130 | tooltipTicksSetter.accept(0); 131 | 132 | if (!tooltip.getString().isEmpty() && tooltipTicks >= 45) { 133 | var wrappedTooltipText = Minecraft.getInstance().font.wrapLines(tooltip, Math.max(widget.getWidth() * 2 / 3, 200)); 134 | if (widget.isMouseHovered()) 135 | create(mouseX, mouseY, wrappedTooltipText).queue(); 136 | else if (widget.isFocused()) 137 | create(widget.getX() - 12, widget.getY() + widget.getHeight() + 16, 138 | wrappedTooltipText) 139 | .queue(); 140 | } 141 | }); 142 | } 143 | } 144 | 145 | /** 146 | * Sets whether tooltip rendering is delayed or not. 147 | * 148 | * @param delayed true if tooltip rendering is delayed 149 | */ 150 | @ApiStatus.Internal 151 | static void setDelayedRender(boolean delayed) { 152 | Tooltip.delayed = delayed; 153 | } 154 | 155 | /** 156 | * Renders all the tooltips. 157 | * 158 | * @param graphics the GUI graphics to render from 159 | */ 160 | public static void renderAll(GuiGraphics graphics) { 161 | if (delayed) 162 | return; 163 | synchronized (TOOLTIPS) { 164 | Tooltip tooltip; 165 | 166 | while ((tooltip = TOOLTIPS.poll()) != null) 167 | tooltip.render(graphics); 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/Tooltipable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui; 11 | 12 | import net.minecraft.network.chat.Text; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | import java.util.Optional; 16 | 17 | /** 18 | * Represents an object which can show a tooltip. 19 | * 20 | * @author LambdAurora 21 | * @version 3.3.0 22 | * @since 1.0.0 23 | */ 24 | public interface Tooltipable { 25 | /** 26 | * Gets the tooltip. 27 | * 28 | * @return the tooltip to show 29 | */ 30 | Optional getTooltip(); 31 | 32 | /** 33 | * Sets the tooltip. 34 | * 35 | * @param tooltip the tooltip to show 36 | */ 37 | void setTooltip(@Nullable Text tooltip); 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/background/Background.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.background; 11 | 12 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 13 | import net.minecraft.client.gui.GuiGraphics; 14 | 15 | /** 16 | * Represents a background which can be rendered on a widget. 17 | * 18 | * @author LambdAurora 19 | * @version 5.0.0 20 | * @since 2.0.0 21 | */ 22 | public interface Background { 23 | void render(GuiGraphics graphics, SpruceWidget widget, int vOffset, int mouseX, int mouseY, float delta); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/background/DirtTexturedBackground.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.background; 11 | 12 | import dev.lambdaurora.spruceui.util.RenderUtil; 13 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 14 | import net.minecraft.client.gui.GuiGraphics; 15 | 16 | public record DirtTexturedBackground(int red, int green, int blue, int alpha) implements Background { 17 | public static final Background NORMAL = new DirtTexturedBackground(64, 64, 64, 255); 18 | public static final Background DARKENED = new DirtTexturedBackground(32, 32, 32, 255); 19 | 20 | @Override 21 | public void render(GuiGraphics graphics, SpruceWidget widget, int vOffset, int mouseX, int mouseY, float delta) { 22 | RenderUtil.renderBackgroundTexture(graphics, 23 | widget.getX(), widget.getY(), widget.getWidth(), widget.getHeight(), 24 | vOffset / 32.f, this.red, this.green, this.blue, this.alpha 25 | ); 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return "DirtTexturedBackground{" + 31 | "red=" + this.red + 32 | ", green=" + this.green + 33 | ", blue=" + this.blue + 34 | ", alpha=" + this.alpha + 35 | '}'; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/background/EmptyBackground.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.background; 11 | 12 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 13 | import net.minecraft.client.gui.GuiGraphics; 14 | 15 | /** 16 | * Represents an empty background. 17 | * 18 | * @author LambdAurora 19 | * @version 5.0.0 20 | * @since 2.0.0 21 | */ 22 | public final class EmptyBackground implements Background { 23 | public static final EmptyBackground EMPTY_BACKGROUND = new EmptyBackground(); 24 | 25 | private EmptyBackground() { 26 | } 27 | 28 | @Override 29 | public void render(GuiGraphics graphics, SpruceWidget widget, int vOffset, int mouseX, int mouseY, float delta) { 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "EmptyBackground{}"; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/background/MenuBackground.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2024 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.background; 11 | 12 | import dev.lambdaurora.spruceui.SpruceTextures; 13 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 14 | import dev.lambdaurora.spruceui.widget.WithBorder; 15 | import net.minecraft.client.Minecraft; 16 | import net.minecraft.client.gui.GuiGraphics; 17 | import net.minecraft.client.renderer.RenderType; 18 | import net.minecraft.resources.Identifier; 19 | 20 | /** 21 | * Represents a background used for menus. 22 | * 23 | * @param texture the texture used for the background 24 | * @param inWorldTexture the textured used for the background when playing in a world 25 | * @author LambdAurora 26 | * @version 6.0.0 27 | * @since 5.1.0 28 | */ 29 | public record MenuBackground(Identifier texture, Identifier inWorldTexture) implements Background { 30 | private static final Minecraft CLIENT = Minecraft.getInstance(); 31 | 32 | public static final MenuBackground MENU_LIST = new MenuBackground( 33 | SpruceTextures.MENU_LIST_BACKGROUND, 34 | SpruceTextures.INWORLD_MENU_LIST_BACKGROUND 35 | ); 36 | 37 | @Override 38 | public void render(GuiGraphics graphics, SpruceWidget widget, int vOffset, int mouseX, int mouseY, float delta) { 39 | int x = widget.getX(); 40 | int y = widget.getY(); 41 | int width = widget.getWidth(); 42 | int height = widget.getHeight(); 43 | 44 | if (widget instanceof WithBorder withBorder) { 45 | var border = withBorder.getBorder(); 46 | 47 | x += border.getLeft(); 48 | y += border.getTop(); 49 | 50 | width -= border.getLeft() + border.getRight(); 51 | height -= border.getTop() + border.getBottom(); 52 | } 53 | 54 | Identifier identifier = CLIENT.level == null ? this.inWorldTexture : this.texture; 55 | graphics.drawTexture( 56 | RenderType::guiTextured, identifier, 57 | x, y, 58 | 0, 0, 59 | width, height, 60 | 16, 16 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/background/SimpleColorBackground.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.background; 11 | 12 | import dev.lambdaurora.spruceui.util.ColorUtil; 13 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 14 | import net.minecraft.client.gui.GuiGraphics; 15 | 16 | public class SimpleColorBackground implements Background { 17 | private final int color; 18 | 19 | public SimpleColorBackground(int color) { 20 | this.color = color; 21 | } 22 | 23 | public SimpleColorBackground(int red, int green, int blue, int alpha) { 24 | this(ColorUtil.packARGBColor(red, green, blue, alpha)); 25 | } 26 | 27 | @Override 28 | public void render(GuiGraphics graphics, SpruceWidget widget, int vOffset, int mouseX, int mouseY, float delta) { 29 | int x = widget.getX(); 30 | int y = widget.getY(); 31 | graphics.fill(x, y, x + widget.getWidth(), y + widget.getHeight(), this.color); 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "SimpleColorBackground{" + 37 | ", color=" + this.color + 38 | '}'; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/border/Border.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.border; 11 | 12 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 13 | import net.minecraft.client.gui.GuiGraphics; 14 | 15 | /** 16 | * Represents a border to draw around a widget. 17 | * 18 | * @author LambdAurora 19 | * @version 5.1.0 20 | * @since 2.0.0 21 | */ 22 | public interface Border { 23 | void render(GuiGraphics graphics, SpruceWidget widget, int mouseX, int mouseY, float delta); 24 | 25 | /** 26 | * Returns the thickness of the top border. 27 | * 28 | * @return the thickness 29 | * @since 5.1.0 30 | */ 31 | default int getTop() { 32 | return this.getThickness(); 33 | } 34 | 35 | /** 36 | * Returns the thickness of the right border. 37 | * 38 | * @return the thickness 39 | * @since 5.1.0 40 | */ 41 | default int getRight() { 42 | return this.getThickness(); 43 | } 44 | 45 | /** 46 | * Returns the thickness of the bottom border. 47 | * 48 | * @return the thickness 49 | * @since 5.1.0 50 | */ 51 | default int getBottom() { 52 | return this.getThickness(); 53 | } 54 | 55 | /** 56 | * Returns the thickness of the left border. 57 | * 58 | * @return the thickness 59 | * @since 5.1.0 60 | */ 61 | default int getLeft() { 62 | return this.getThickness(); 63 | } 64 | 65 | /** 66 | * Returns the thickness of the border. 67 | * 68 | * @return the thickness 69 | */ 70 | int getThickness(); 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/border/EmptyBorder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.border; 11 | 12 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 13 | import net.minecraft.client.gui.GuiGraphics; 14 | 15 | /** 16 | * Represents an empty border. 17 | * 18 | * @author LambdAurora 19 | * @version 5.0.0 20 | * @since 2.0.0 21 | */ 22 | public final class EmptyBorder implements Border { 23 | public static final EmptyBorder EMPTY_BORDER = new EmptyBorder(); 24 | 25 | private EmptyBorder() { 26 | } 27 | 28 | @Override 29 | public void render(GuiGraphics graphics, SpruceWidget widget, int mouseX, int mouseY, float delta) { 30 | } 31 | 32 | @Override 33 | public int getThickness() { 34 | return 0; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "EmptyBorder{}"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/border/MenuBorder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2024 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.border; 11 | 12 | import dev.lambdaurora.spruceui.SpruceTextures; 13 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 14 | import net.minecraft.client.Minecraft; 15 | import net.minecraft.client.gui.GuiGraphics; 16 | import net.minecraft.client.renderer.RenderType; 17 | import net.minecraft.resources.Identifier; 18 | 19 | /** 20 | * Represents a typical menu border. 21 | * 22 | * @param top {@code true} if a top border is present, or {@code false} otherwise 23 | * @param right {@code true} if a right border is present, or {@code false} otherwise 24 | * @param bottom {@code true} if a bottom border is present, or {@code false} otherwise 25 | * @param left {@code true} if a left border is present, or {@code false} otherwise 26 | * @author LambdAurora 27 | * @version 7.0.0 28 | * @since 5.1.0 29 | */ 30 | public record MenuBorder(boolean top, boolean right, boolean bottom, boolean left) implements Border { 31 | private static final Minecraft CLIENT = Minecraft.getInstance(); 32 | private static final int THICKNESS = 2; 33 | 34 | public static final MenuBorder LIST = new MenuBorder(true, false, true, false); 35 | public static final MenuBorder TAB_LIST = new MenuBorder(true, true, true, false); 36 | 37 | @Override 38 | public void render(GuiGraphics graphics, SpruceWidget widget, int mouseX, int mouseY, float delta) { 39 | if (this.top) { 40 | Identifier topTexture = CLIENT.level == null ? SpruceTextures.MENU_TOP_BORDER : SpruceTextures.INWORLD_MENU_TOP_BORDER; 41 | 42 | int width = widget.getWidth(); 43 | 44 | if (this.right) { 45 | width -= THICKNESS; 46 | } 47 | 48 | graphics.drawTexture( 49 | RenderType::guiTextured, 50 | topTexture, 51 | widget.getX(), widget.getY(), 52 | 0, 0, 53 | width, THICKNESS, 54 | 32, THICKNESS 55 | ); 56 | } 57 | 58 | if (this.top && this.right) { 59 | Identifier cornerTexture = CLIENT.level == null ? SpruceTextures.MENU_TOP_RIGHT_BORDER : SpruceTextures.INWORLD_MENU_TOP_RIGHT_BORDER; 60 | graphics.drawTexture( 61 | RenderType::guiTextured, 62 | cornerTexture, 63 | widget.getEndX() - THICKNESS, widget.getY(), 64 | 0, 0, 65 | THICKNESS, THICKNESS, 66 | THICKNESS, THICKNESS 67 | ); 68 | } 69 | 70 | if (this.right) { 71 | Identifier rightTexture = CLIENT.level == null ? SpruceTextures.MENU_RIGHT_BORDER : SpruceTextures.INWORLD_MENU_RIGHT_BORDER; 72 | 73 | int y = widget.getY(); 74 | int height = widget.getHeight(); 75 | 76 | if (this.top) { 77 | y += THICKNESS; 78 | height -= THICKNESS; 79 | } 80 | 81 | if (this.bottom) { 82 | height -= THICKNESS; 83 | } 84 | 85 | graphics.drawTexture( 86 | RenderType::guiTextured, rightTexture, 87 | widget.getEndX() - THICKNESS, y, 88 | 0, 0, 89 | THICKNESS, height, 90 | THICKNESS, 32 91 | ); 92 | } 93 | 94 | if (this.bottom && this.right) { 95 | Identifier cornerTexture = CLIENT.level == null 96 | ? SpruceTextures.MENU_BOTTOM_RIGHT_BORDER : SpruceTextures.INWORLD_MENU_BOTTOM_RIGHT_BORDER; 97 | graphics.drawTexture( 98 | RenderType::guiTextured, 99 | cornerTexture, 100 | widget.getEndX() - THICKNESS, widget.getEndY() - THICKNESS, 101 | 0, 0, 102 | THICKNESS, THICKNESS, 103 | THICKNESS, THICKNESS 104 | ); 105 | } 106 | 107 | if (this.bottom) { 108 | Identifier bottomTexture = CLIENT.level == null ? SpruceTextures.MENU_BOTTOM_BORDER : SpruceTextures.INWORLD_MENU_BOTTOM_BORDER; 109 | 110 | int width = widget.getWidth(); 111 | 112 | if (this.right) { 113 | width -= THICKNESS; 114 | } 115 | 116 | graphics.drawTexture( 117 | RenderType::guiTextured, 118 | bottomTexture, 119 | widget.getX(), widget.getEndY() - THICKNESS, 120 | 0, 0, 121 | width, THICKNESS, 122 | 32, THICKNESS 123 | ); 124 | } 125 | } 126 | 127 | @Override 128 | public int getTop() { 129 | return this.top ? THICKNESS : 0; 130 | } 131 | 132 | @Override 133 | public int getRight() { 134 | return this.right ? THICKNESS : 0; 135 | } 136 | 137 | @Override 138 | public int getBottom() { 139 | return this.bottom ? THICKNESS : 0; 140 | } 141 | 142 | @Override 143 | public int getLeft() { 144 | return 0; 145 | } 146 | 147 | @Override 148 | public int getThickness() { 149 | return THICKNESS; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/border/SimpleBorder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.border; 11 | 12 | import dev.lambdaurora.spruceui.util.ColorUtil; 13 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 14 | import net.minecraft.client.gui.GuiGraphics; 15 | 16 | /** 17 | * Represents a simple solid border to draw around a widget. 18 | * 19 | * @author LambdAurora 20 | * @version 6.0.0 21 | * @since 6.0.0 22 | */ 23 | public final class SimpleBorder implements Border { 24 | public static final SimpleBorder SIMPLE_BORDER = new SimpleBorder(1, 192, 192, 192, 255); 25 | 26 | private final int thickness; 27 | private final int color; 28 | private final int focusedColor; 29 | 30 | public SimpleBorder(int thickness, int color) { 31 | this(thickness, color, color); 32 | } 33 | 34 | public SimpleBorder(int thickness, int color, int focusedColor) { 35 | this.thickness = thickness; 36 | this.color = color; 37 | this.focusedColor = color; 38 | } 39 | 40 | public SimpleBorder(int thickness, int red, int green, int blue, int alpha) { 41 | this(thickness, red, green, blue, alpha, red, green, blue, alpha); 42 | } 43 | 44 | public SimpleBorder(int thickness, int red, int green, int blue, int alpha, int focusedRed, int focusedGreen, int focusedBlue, int focusedAlpha) { 45 | this.thickness = thickness; 46 | this.color = ColorUtil.packARGBColor(red, green, blue, alpha); 47 | this.focusedColor = ColorUtil.packARGBColor(focusedRed, focusedGreen, focusedBlue, focusedAlpha); 48 | } 49 | 50 | @Override 51 | public void render(GuiGraphics graphics, SpruceWidget widget, int mouseX, int mouseY, float delta) { 52 | int x = widget.getX(); 53 | int y = widget.getY(); 54 | int right = x + widget.getWidth(); 55 | int bottom = y + widget.getHeight(); 56 | int color = widget.isFocused() ? this.focusedColor : this.color; 57 | // Top border 58 | graphics.fill(x, y, right, y + this.thickness, color); 59 | // Right border 60 | graphics.fill(right - this.thickness, y, right, bottom, color); 61 | // Bottom 62 | graphics.fill(x, bottom, right, bottom - this.thickness, color); 63 | // Left border 64 | graphics.fill(x, y, x + this.thickness, bottom, color); 65 | } 66 | 67 | @Override 68 | public int getThickness() { 69 | return this.thickness; 70 | } 71 | 72 | @Override 73 | public String toString() { 74 | return "SimpleBorder{" + 75 | "thickness=" + this.thickness + 76 | ", color=" + Integer.toHexString(this.color) + 77 | ", focusedColor=" + Integer.toHexString(this.focusedColor) + 78 | '}'; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/border/TexturedBorder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2024 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.border; 11 | 12 | import dev.lambdaurora.spruceui.SpruceTextures; 13 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 14 | import net.minecraft.client.gui.GuiGraphics; 15 | import net.minecraft.client.gui.components.WidgetSprites; 16 | import net.minecraft.client.renderer.RenderType; 17 | 18 | /** 19 | * Represents a textured border to draw around a widget. 20 | * 21 | * @author LambdAurora 22 | * @version 6.0.0 23 | * @since 6.0.0 24 | */ 25 | public record TexturedBorder(WidgetSprites sprites) implements Border { 26 | public static final TexturedBorder SIMPLE = new TexturedBorder(new WidgetSprites( 27 | SpruceTextures.SIMPLE_BORDER_SPRITE, 28 | SpruceTextures.SIMPLE_HIGHLIGHTED_BORDER_SPRITE 29 | )); 30 | 31 | @Override 32 | public void render(GuiGraphics graphics, SpruceWidget widget, int mouseX, int mouseY, float delta) { 33 | graphics.drawSprite( 34 | RenderType::guiTextured, this.sprites.get(widget.isActive(), widget.isFocusedOrHovered()), 35 | widget.getX(), widget.getY(), 36 | widget.getWidth(), widget.getHeight() 37 | ); 38 | } 39 | 40 | @Override 41 | public int getThickness() { 42 | return 1; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/event/EventUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.event; 11 | 12 | import net.fabricmc.fabric.api.event.Event; 13 | import net.fabricmc.fabric.api.event.EventFactory; 14 | 15 | /** 16 | * Represents a set of utilities for SpruceUI's events. 17 | * 18 | * @author LambdAurora 19 | * @version 3.0.0 20 | * @since 1.4.0 21 | */ 22 | public final class EventUtil { 23 | private EventUtil() { 24 | throw new UnsupportedOperationException("EventUtil is a singleton."); 25 | } 26 | 27 | static Event makeOpenScreenEvent() { 28 | return EventFactory.createArrayBacked(OpenScreenCallback.class, listeners -> (client, screen) -> { 29 | for (var event : listeners) { 30 | event.apply(client, screen); 31 | } 32 | }); 33 | } 34 | 35 | /** 36 | * Registers a full open screen event. 37 | * 38 | * @param pre Pre open screen callback. 39 | * @param post Post open screen callback. 40 | */ 41 | public static void onOpenScreen(OpenScreenCallback pre, OpenScreenCallback post) { 42 | OpenScreenCallback.PRE.register(pre); 43 | OpenScreenCallback.EVENT.register(post); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/event/OpenScreenCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.event; 11 | 12 | import net.fabricmc.fabric.api.event.Event; 13 | import net.minecraft.client.Minecraft; 14 | import net.minecraft.client.gui.screens.Screen; 15 | import org.jetbrains.annotations.Nullable; 16 | 17 | /** 18 | * Represents an event callback which is fired when an {@link Screen} is opened. 19 | * 20 | * @author LambdAurora 21 | * @version 3.3.0 22 | * @since 1.2.0 23 | */ 24 | @FunctionalInterface 25 | public interface OpenScreenCallback { 26 | Event PRE = EventUtil.makeOpenScreenEvent(); 27 | Event EVENT = EventUtil.makeOpenScreenEvent(); 28 | 29 | void apply(Minecraft client, @Nullable Screen screen); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/event/ResolutionChangeCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.event; 11 | 12 | import net.fabricmc.fabric.api.event.Event; 13 | import net.fabricmc.fabric.api.event.EventFactory; 14 | import net.minecraft.client.Minecraft; 15 | 16 | /** 17 | * Represents an event callback which is fired when the Minecraft's resolution is changed. 18 | * 19 | * @author LambdAurora 20 | * @version 3.3.0 21 | * @since 1.2.0 22 | */ 23 | @FunctionalInterface 24 | public interface ResolutionChangeCallback { 25 | Event EVENT = EventFactory.createArrayBacked(ResolutionChangeCallback.class, listeners -> client -> { 26 | for (var event : listeners) { 27 | event.apply(client); 28 | } 29 | }); 30 | 31 | void apply(Minecraft client); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/mixin/MinecraftClientMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.mixin; 11 | 12 | import dev.lambdaurora.spruceui.event.OpenScreenCallback; 13 | import dev.lambdaurora.spruceui.event.ResolutionChangeCallback; 14 | import net.minecraft.client.Minecraft; 15 | import net.minecraft.client.gui.screens.Screen; 16 | import org.spongepowered.asm.mixin.Mixin; 17 | import org.spongepowered.asm.mixin.injection.At; 18 | import org.spongepowered.asm.mixin.injection.Inject; 19 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 20 | 21 | /** 22 | * Represents the injection point for the {@link OpenScreenCallback} and {@link ResolutionChangeCallback} events. 23 | * 24 | * @author LambdAurora 25 | * @version 3.2.1 26 | * @since 1.2.0 27 | */ 28 | @Mixin(Minecraft.class) 29 | public class MinecraftClientMixin { 30 | @Inject(method = "setScreen", at = @At("HEAD")) 31 | private void onScreenPre(Screen screen, CallbackInfo ci) { 32 | OpenScreenCallback.PRE.invoker().apply((Minecraft) (Object) this, screen); 33 | } 34 | 35 | @Inject(method = "setScreen", at = @At("RETURN")) 36 | private void onScreenChange(Screen screen, CallbackInfo ci) { 37 | OpenScreenCallback.EVENT.invoker().apply((Minecraft) (Object) this, screen); 38 | } 39 | 40 | @Inject(method = "resizeDisplay", at = @At("RETURN")) 41 | private void onResolutionChanged(CallbackInfo ci) { 42 | ResolutionChangeCallback.EVENT.invoker().apply((Minecraft) (Object) this); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/navigation/NavigationDirection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.navigation; 11 | 12 | import org.lwjgl.glfw.GLFW; 13 | 14 | import java.util.Optional; 15 | 16 | /** 17 | * Represents navigation direction. 18 | * 19 | * @author LambdAurora 20 | * @version 3.0.0 21 | * @since 2.0.0 22 | */ 23 | public enum NavigationDirection { 24 | LEFT, 25 | RIGHT, 26 | UP, 27 | DOWN; 28 | 29 | /** 30 | * Returns whether or not this navigation direction is horizontal. 31 | * 32 | * @return true if this direction is horizontal, else false 33 | */ 34 | public boolean isHorizontal() { 35 | return this == LEFT || this == RIGHT; 36 | } 37 | 38 | /** 39 | * Returns whether or not this navigation direction is vertical. 40 | * 41 | * @return true if this direction is vertical, else false 42 | */ 43 | public boolean isVertical() { 44 | return this == UP || this == DOWN; 45 | } 46 | 47 | public boolean isLookingForward() { 48 | return this == DOWN || this == RIGHT; 49 | } 50 | 51 | /** 52 | * Returns a navigation direction from a key. 53 | * 54 | * @param keyCode the key 55 | * @param shift true if the shift key is pressed, else false 56 | * @return the direction if associated to the specified key, else empty 57 | */ 58 | public static Optional fromKey(int keyCode, boolean shift) { 59 | if (shift && keyCode != GLFW.GLFW_KEY_TAB) 60 | return Optional.empty(); 61 | switch (keyCode) { 62 | case GLFW.GLFW_KEY_LEFT: 63 | return Optional.of(LEFT); 64 | case GLFW.GLFW_KEY_RIGHT: 65 | return Optional.of(RIGHT); 66 | case GLFW.GLFW_KEY_UP: 67 | return Optional.of(UP); 68 | case GLFW.GLFW_KEY_TAB: 69 | if (shift) 70 | return Optional.of(UP); 71 | case GLFW.GLFW_KEY_DOWN: 72 | return Optional.of(DOWN); 73 | default: 74 | return Optional.empty(); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/navigation/NavigationUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.navigation; 11 | 12 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 13 | 14 | import java.util.List; 15 | import java.util.function.BooleanSupplier; 16 | import java.util.function.Consumer; 17 | import java.util.function.Supplier; 18 | 19 | /** 20 | * Utilities for handling navigation. 21 | * 22 | * @author LambdAurora 23 | * @version 3.0.0 24 | * @since 2.0.0 25 | */ 26 | public final class NavigationUtils { 27 | private NavigationUtils() { 28 | throw new UnsupportedOperationException("NavigationUtils only contains static definitions."); 29 | } 30 | 31 | public static boolean tryNavigate(NavigationDirection direction, boolean tab, List children, E focused, Consumer setFocused, boolean alwaysFocus) { 32 | if (children.isEmpty()) 33 | return false; 34 | if (!tab && alwaysFocus && focused != null) { 35 | int i = children.indexOf(focused); 36 | if ((!direction.isLookingForward() && i == 0) || (direction.isLookingForward() && i == children.size() - 1)) { 37 | boolean result = focused.onNavigation(direction, false); 38 | focused.setFocused(true); 39 | return result; 40 | } 41 | } 42 | if (focused == null || !focused.onNavigation(direction, tab)) { 43 | int i = children.indexOf(focused); 44 | int next; 45 | if (focused != null && i >= 0) next = i + (direction.isLookingForward() ? 1 : 0); 46 | else if (direction.isLookingForward()) next = 0; 47 | else next = children.size(); 48 | 49 | var iterator = children.listIterator(next); 50 | BooleanSupplier hasNext = direction.isLookingForward() ? iterator::hasNext : iterator::hasPrevious; 51 | Supplier nextGetter = direction.isLookingForward() ? iterator::next : iterator::previous; 52 | 53 | E nextElement; 54 | do { 55 | if (!hasNext.getAsBoolean()) { 56 | setFocused.accept(null); 57 | return false; 58 | } 59 | 60 | nextElement = nextGetter.get(); 61 | } while (!nextElement.onNavigation(direction, tab)); 62 | 63 | setFocused.accept(nextElement); 64 | } 65 | return true; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/option/SpruceBooleanOption.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.option; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.SpruceTexts; 14 | import dev.lambdaurora.spruceui.widget.SpruceButtonWidget; 15 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 16 | import net.minecraft.TextFormatting; 17 | import net.minecraft.network.chat.Text; 18 | import org.jetbrains.annotations.Nullable; 19 | 20 | import java.util.function.Consumer; 21 | import java.util.function.Supplier; 22 | 23 | /** 24 | * Represents a boolean option. 25 | *

26 | * Works the same as the vanilla one but can provide a tooltip. 27 | * 28 | * @author LambdAurora 29 | * @version 3.3.0 30 | * @since 1.0.0 31 | */ 32 | public class SpruceBooleanOption extends SpruceOption { 33 | private final Supplier getter; 34 | private final Consumer setter; 35 | private final boolean colored; 36 | 37 | public SpruceBooleanOption(String key, Supplier getter, Consumer setter, @Nullable Text tooltip) { 38 | this(key, getter, setter, tooltip, false); 39 | } 40 | 41 | public SpruceBooleanOption(String key, Supplier getter, Consumer setter, @Nullable Text tooltip, boolean colored) { 42 | super(key); 43 | this.getter = getter; 44 | this.setter = setter; 45 | this.colored = colored; 46 | this.setTooltip(tooltip); 47 | } 48 | 49 | public void set(String value) { 50 | this.set("true".equals(value)); 51 | } 52 | 53 | public void set() { 54 | this.set(!this.get()); 55 | } 56 | 57 | private void set(boolean value) { 58 | this.setter.accept(value); 59 | } 60 | 61 | /** 62 | * Gets the current value. 63 | * 64 | * @return the current value 65 | */ 66 | public boolean get() { 67 | return this.getter.get(); 68 | } 69 | 70 | /** 71 | * Returns whether the option value is colored or not. 72 | * 73 | * @return {@code true} if the option value is colored, else {@code false} 74 | */ 75 | public boolean isColored() { 76 | return this.colored; 77 | } 78 | 79 | @Override 80 | public SpruceWidget createWidget(Position position, int width) { 81 | var button = new SpruceButtonWidget(position, width, 20, this.getDisplayText(), btn -> { 82 | this.set(); 83 | btn.setMessage(this.getDisplayText()); 84 | }); 85 | this.getOptionTooltip().ifPresent(button::setTooltip); 86 | return button; 87 | } 88 | 89 | /** 90 | * Gets the display string. 91 | * 92 | * @return the display string 93 | */ 94 | public Text getDisplayText() { 95 | boolean value = this.get(); 96 | var toggleText = SpruceTexts.getToggleText(value); 97 | if (this.colored) 98 | toggleText = toggleText.copy().setStyle(toggleText.getStyle().withColor(value ? TextFormatting.GREEN : TextFormatting.RED)); 99 | return this.getDisplayText(toggleText); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/option/SpruceCheckboxBooleanOption.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.option; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.widget.SpruceCheckboxWidget; 14 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 15 | import net.minecraft.network.chat.Text; 16 | import org.jetbrains.annotations.Nullable; 17 | 18 | import java.util.function.Consumer; 19 | import java.util.function.Supplier; 20 | 21 | /** 22 | * Represents a boolean option. 23 | *

24 | * Works the as {@link SpruceBooleanOption} but uses a checkbox instead. 25 | * 26 | * @author LambdAurora 27 | * @version 3.3.0 28 | * @since 1.6.0 29 | */ 30 | public class SpruceCheckboxBooleanOption extends SpruceBooleanOption { 31 | public SpruceCheckboxBooleanOption(String key, Supplier getter, Consumer setter, @Nullable Text tooltip) { 32 | super(key, getter, setter, tooltip); 33 | } 34 | 35 | public SpruceCheckboxBooleanOption(String key, Supplier getter, Consumer setter, @Nullable Text tooltip, boolean colored) { 36 | super(key, getter, setter, tooltip, colored); 37 | } 38 | 39 | @Override 40 | public SpruceWidget createWidget(Position position, int width) { 41 | var button = new SpruceCheckboxWidget(position, width, 20, this.getDisplayText(), (btn, newValue) -> { 42 | this.set(); 43 | btn.setMessage(this.getDisplayText()); 44 | }, this.get()); 45 | button.setColored(this.isColored()); 46 | this.getOptionTooltip().ifPresent(button::setTooltip); 47 | return button; 48 | } 49 | 50 | @Override 51 | public Text getDisplayText() { 52 | return this.getPrefix(); 53 | } 54 | 55 | @Override 56 | public Text getDisplayText(Text value) { 57 | return this.getPrefix(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/option/SpruceCyclingOption.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.option; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.widget.SpruceButtonWidget; 14 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 15 | import net.minecraft.network.chat.Text; 16 | import org.jetbrains.annotations.Nullable; 17 | 18 | import java.util.function.Consumer; 19 | import java.util.function.Function; 20 | 21 | /** 22 | * Represents a cycling option. 23 | *

24 | * Works the same as the vanilla one but can provide a tooltip. 25 | * 26 | * @author LambdAurora 27 | * @version 3.0.0 28 | * @since 1.0.0 29 | */ 30 | public class SpruceCyclingOption extends SpruceOption { 31 | private final Consumer setter; 32 | private final Function messageProvider; 33 | 34 | public SpruceCyclingOption(String key, Consumer setter, Function messageProvider, @Nullable Text tooltip) { 35 | super(key); 36 | this.setter = setter; 37 | this.messageProvider = messageProvider; 38 | this.setTooltip(tooltip); 39 | } 40 | 41 | /** 42 | * Cycles the option. 43 | * 44 | * @param amount The amount to cycle. 45 | */ 46 | public void cycle(int amount) { 47 | this.setter.accept(amount); 48 | } 49 | 50 | @Override 51 | public SpruceWidget createWidget(Position position, int width) { 52 | var button = new SpruceButtonWidget(position, width, 20, this.getMessage(), btn -> { 53 | this.cycle(1); 54 | btn.setMessage(this.getMessage()); 55 | }); 56 | this.getOptionTooltip().ifPresent(button::setTooltip); 57 | return button; 58 | } 59 | 60 | /** 61 | * Gets the option message. 62 | * 63 | * @return The option message. 64 | */ 65 | public Text getMessage() { 66 | return this.messageProvider.apply(this); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/option/SpruceDoubleInputOption.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.option; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 14 | import dev.lambdaurora.spruceui.widget.text.SpruceNamedTextFieldWidget; 15 | import dev.lambdaurora.spruceui.widget.text.SpruceTextFieldWidget; 16 | import net.minecraft.TextFormatting; 17 | import net.minecraft.network.chat.Style; 18 | import net.minecraft.network.chat.Text; 19 | import net.minecraft.util.FormattedCharSequence; 20 | import org.jetbrains.annotations.Nullable; 21 | 22 | import java.util.function.Consumer; 23 | import java.util.function.Supplier; 24 | 25 | /** 26 | * Represents a double input option. 27 | * 28 | * @author LambdAurora 29 | * @version 3.0.0 30 | * @since 2.1.0 31 | */ 32 | public class SpruceDoubleInputOption extends SpruceOption { 33 | private final Supplier getter; 34 | private final Consumer setter; 35 | 36 | public SpruceDoubleInputOption(String key, Supplier getter, Consumer setter, @Nullable Text tooltip) { 37 | super(key); 38 | this.getter = getter; 39 | this.setter = setter; 40 | this.setTooltip(tooltip); 41 | } 42 | 43 | @Override 44 | public SpruceWidget createWidget(Position position, int width) { 45 | var textField = new SpruceTextFieldWidget(position, width, 20, this.getPrefix()); 46 | textField.setText(String.valueOf(this.get())); 47 | textField.setTextPredicate(SpruceTextFieldWidget.DOUBLE_INPUT_PREDICATE); 48 | textField.setRenderTextProvider((displayedText, offset) -> { 49 | try { 50 | Double.parseDouble(textField.getText()); 51 | return FormattedCharSequence.forward(displayedText, Style.EMPTY); 52 | } catch (NumberFormatException e) { 53 | return FormattedCharSequence.forward(displayedText, Style.EMPTY.withColor(TextFormatting.RED)); 54 | } 55 | }); 56 | textField.setChangedListener(input -> { 57 | double value; 58 | try { 59 | value = Double.parseDouble(input); 60 | } catch (NumberFormatException e) { 61 | value = 0; 62 | } 63 | this.set(value); 64 | }); 65 | this.getOptionTooltip().ifPresent(textField::setTooltip); 66 | return new SpruceNamedTextFieldWidget(textField); 67 | } 68 | 69 | public void set(double value) { 70 | this.setter.accept(value); 71 | } 72 | 73 | /** 74 | * Gets the current value. 75 | * 76 | * @return the current value 77 | */ 78 | public double get() { 79 | return this.getter.get(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/option/SpruceDoubleOption.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.option; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 14 | import dev.lambdaurora.spruceui.widget.option.SpruceOptionSliderWidget; 15 | import net.minecraft.network.chat.Text; 16 | import net.minecraft.util.math.MathHelper; 17 | import org.jetbrains.annotations.Nullable; 18 | 19 | import java.util.function.Consumer; 20 | import java.util.function.Function; 21 | import java.util.function.Supplier; 22 | 23 | /** 24 | * Represents a double option. 25 | *

26 | * Works the same as the vanilla one but can provide a tooltip. 27 | * 28 | * @author LambdAurora 29 | * @version 3.0.0 30 | * @since 1.0.0 31 | */ 32 | public class SpruceDoubleOption extends SpruceOption { 33 | protected final float step; 34 | protected final double min; 35 | protected double max; 36 | private final Supplier getter; 37 | private final Consumer setter; 38 | private final Function displayStringGetter; 39 | 40 | public SpruceDoubleOption(String key, double min, double max, float step, Supplier getter, Consumer setter, Function displayStringGetter, @Nullable Text tooltip) { 41 | super(key); 42 | this.min = min; 43 | this.max = max; 44 | this.step = step; 45 | this.getter = getter; 46 | this.setter = setter; 47 | this.displayStringGetter = displayStringGetter; 48 | this.setTooltip(tooltip); 49 | } 50 | 51 | @Override 52 | public SpruceWidget createWidget(Position position, int width) { 53 | var slider = new SpruceOptionSliderWidget(position, width, 20, this); 54 | this.getOptionTooltip().ifPresent(slider::setTooltip); 55 | return slider; 56 | } 57 | 58 | public double getRatio(double value) { 59 | return MathHelper.clamp((this.adjust(value) - this.min) / (this.max - this.min), 0.0D, 1.0D); 60 | } 61 | 62 | public double getValue(double ratio) { 63 | return this.adjust(MathHelper.lerp(MathHelper.clamp(ratio, 0.0D, 1.0D), this.min, this.max)); 64 | } 65 | 66 | private double adjust(double value) { 67 | if (this.step > 0.0F) { 68 | value = this.step * (float) Math.round(value / (double) this.step); 69 | } 70 | 71 | return MathHelper.clamp(value, this.min, this.max); 72 | } 73 | 74 | public double getMin() { 75 | return this.min; 76 | } 77 | 78 | public double getMax() { 79 | return this.max; 80 | } 81 | 82 | public void setMax(float max) { 83 | this.max = max; 84 | } 85 | 86 | public void set(double value) { 87 | this.setter.accept(value); 88 | } 89 | 90 | /** 91 | * Gets the current value. 92 | * 93 | * @return the current value 94 | */ 95 | public double get() { 96 | return this.getter.get(); 97 | } 98 | 99 | /** 100 | * Gets the display string. 101 | * 102 | * @return the display string 103 | */ 104 | public Text getDisplayString() { 105 | return this.displayStringGetter.apply(this); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/option/SpruceFloatInputOption.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.option; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 14 | import dev.lambdaurora.spruceui.widget.text.SpruceNamedTextFieldWidget; 15 | import dev.lambdaurora.spruceui.widget.text.SpruceTextFieldWidget; 16 | import net.minecraft.TextFormatting; 17 | import net.minecraft.network.chat.Style; 18 | import net.minecraft.network.chat.Text; 19 | import net.minecraft.util.FormattedCharSequence; 20 | import org.jetbrains.annotations.Nullable; 21 | 22 | import java.util.function.Consumer; 23 | import java.util.function.Supplier; 24 | 25 | /** 26 | * Represents a float input option. 27 | * 28 | * @author LambdAurora 29 | * @version 3.0.0 30 | * @since 2.1.0 31 | */ 32 | public class SpruceFloatInputOption extends SpruceOption { 33 | private final Supplier getter; 34 | private final Consumer setter; 35 | 36 | public SpruceFloatInputOption(String key, Supplier getter, Consumer setter, @Nullable Text tooltip) { 37 | super(key); 38 | this.getter = getter; 39 | this.setter = setter; 40 | this.setTooltip(tooltip); 41 | } 42 | 43 | @Override 44 | public SpruceWidget createWidget(Position position, int width) { 45 | var textField = new SpruceTextFieldWidget(position, width, 20, this.getPrefix()); 46 | textField.setText(String.valueOf(this.get())); 47 | textField.setTextPredicate(SpruceTextFieldWidget.FLOAT_INPUT_PREDICATE); 48 | textField.setRenderTextProvider((displayedText, offset) -> { 49 | try { 50 | Float.parseFloat(textField.getText()); 51 | return FormattedCharSequence.forward(displayedText, Style.EMPTY); 52 | } catch (NumberFormatException e) { 53 | return FormattedCharSequence.forward(displayedText, Style.EMPTY.withColor(TextFormatting.RED)); 54 | } 55 | }); 56 | textField.setChangedListener(input -> { 57 | float value; 58 | try { 59 | value = Float.parseFloat(input); 60 | } catch (NumberFormatException e) { 61 | value = 0; 62 | } 63 | this.set(value); 64 | }); 65 | this.getOptionTooltip().ifPresent(textField::setTooltip); 66 | return new SpruceNamedTextFieldWidget(textField); 67 | } 68 | 69 | public void set(float value) { 70 | this.setter.accept(value); 71 | } 72 | 73 | /** 74 | * Gets the current value. 75 | * 76 | * @return the current value 77 | */ 78 | public float get() { 79 | return this.getter.get(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/option/SpruceIntegerInputOption.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.option; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.util.SpruceUtil; 14 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 15 | import dev.lambdaurora.spruceui.widget.text.SpruceNamedTextFieldWidget; 16 | import dev.lambdaurora.spruceui.widget.text.SpruceTextFieldWidget; 17 | import net.minecraft.TextFormatting; 18 | import net.minecraft.network.chat.Style; 19 | import net.minecraft.network.chat.Text; 20 | import net.minecraft.util.FormattedCharSequence; 21 | import org.jetbrains.annotations.Nullable; 22 | 23 | import java.util.function.Consumer; 24 | import java.util.function.Supplier; 25 | 26 | /** 27 | * Represents an integer input option. 28 | * 29 | * @author LambdAurora 30 | * @version 3.2.1 31 | * @since 2.1.0 32 | */ 33 | public class SpruceIntegerInputOption extends SpruceOption { 34 | private final Supplier getter; 35 | private final Consumer setter; 36 | 37 | public SpruceIntegerInputOption(String key, Supplier getter, Consumer setter, @Nullable Text tooltip) { 38 | super(key); 39 | this.getter = getter; 40 | this.setter = setter; 41 | this.setTooltip(tooltip); 42 | } 43 | 44 | @Override 45 | public SpruceWidget createWidget(Position position, int width) { 46 | var textField = new SpruceTextFieldWidget(position, width, 20, this.getPrefix()); 47 | textField.setText(String.valueOf(this.get())); 48 | textField.setTextPredicate(SpruceTextFieldWidget.INTEGER_INPUT_PREDICATE); 49 | textField.setRenderTextProvider((displayedText, offset) -> { 50 | try { 51 | Integer.parseInt(textField.getText()); 52 | return FormattedCharSequence.forward(displayedText, Style.EMPTY); 53 | } catch (NumberFormatException e) { 54 | return FormattedCharSequence.forward(displayedText, Style.EMPTY.withColor(TextFormatting.RED)); 55 | } 56 | }); 57 | textField.setChangedListener(input -> { 58 | int value = SpruceUtil.parseIntFromString(input); 59 | this.set(value); 60 | }); 61 | this.getOptionTooltip().ifPresent(textField::setTooltip); 62 | return new SpruceNamedTextFieldWidget(textField); 63 | } 64 | 65 | public void set(int value) { 66 | this.setter.accept(value); 67 | } 68 | 69 | /** 70 | * Gets the current value. 71 | * 72 | * @return the current value 73 | */ 74 | public int get() { 75 | return this.getter.get(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/option/SpruceOption.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.option; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.util.Nameable; 14 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 15 | import net.minecraft.client.resources.language.I18n; 16 | import net.minecraft.network.chat.Text; 17 | import org.jetbrains.annotations.Nullable; 18 | 19 | import java.util.Objects; 20 | import java.util.Optional; 21 | 22 | /** 23 | * Represents an option. 24 | * 25 | * @author LambdAurora 26 | * @version 3.3.0 27 | * @since 1.0.3 28 | */ 29 | public abstract class SpruceOption implements Nameable { 30 | public final String key; 31 | private Optional tooltip = Optional.empty(); 32 | 33 | public SpruceOption(String key) { 34 | Objects.requireNonNull(key, "Cannot create an option without a key."); 35 | this.key = key; 36 | } 37 | 38 | @Override 39 | public String getName() { 40 | return I18n.get(this.key); 41 | } 42 | 43 | public Optional getOptionTooltip() { 44 | return this.tooltip; 45 | } 46 | 47 | public void setTooltip(@Nullable Text tooltip) { 48 | this.tooltip = Optional.ofNullable(tooltip); 49 | } 50 | 51 | /** 52 | * Returns the display prefix text. 53 | * 54 | * @return the display prefix 55 | */ 56 | public Text getPrefix() { 57 | return Text.translatable(this.key); 58 | } 59 | 60 | /** 61 | * Returns the display text. 62 | * 63 | * @param value the value 64 | * @return the display text 65 | */ 66 | public Text getDisplayText(Text value) { 67 | return Text.translatable("spruceui.options.generic", this.getPrefix(), value); 68 | } 69 | 70 | public abstract SpruceWidget createWidget(Position position, int width); 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/option/SpruceSeparatorOption.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.option; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.widget.SpruceSeparatorWidget; 14 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 15 | import net.minecraft.network.chat.Text; 16 | import org.jetbrains.annotations.Nullable; 17 | 18 | /** 19 | * Represents a separator option. 20 | * 21 | * @author LambdAurora 22 | * @version 3.0.0 23 | * @since 1.0.1 24 | */ 25 | public class SpruceSeparatorOption extends SpruceOption { 26 | private final boolean showTitle; 27 | 28 | public SpruceSeparatorOption(String key, boolean showTitle, @Nullable Text tooltip) { 29 | super(key); 30 | this.showTitle = showTitle; 31 | this.setTooltip(tooltip); 32 | } 33 | 34 | @Override 35 | public SpruceWidget createWidget(Position position, int width) { 36 | var separator = new SpruceSeparatorWidget(position, width, this.showTitle ? Text.translatable(this.key) : null); 37 | this.getOptionTooltip().ifPresent(separator::setTooltip); 38 | return separator; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/option/SpruceSimpleActionOption.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.option; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.widget.SpruceButtonWidget; 14 | import dev.lambdaurora.spruceui.widget.SpruceTexturedButtonWidget; 15 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 16 | import net.minecraft.network.chat.Text; 17 | import net.minecraft.resources.Identifier; 18 | import org.jetbrains.annotations.Nullable; 19 | 20 | /** 21 | * Represents an option with a simple action. 22 | * 23 | * @author LambdAurora 24 | * @version 3.2.1 25 | * @since 1.0.1 26 | */ 27 | public final class SpruceSimpleActionOption extends SpruceOption { 28 | private final ButtonFactory buttonFactory; 29 | private final SpruceButtonWidget.PressAction action; 30 | 31 | public SpruceSimpleActionOption(String key, ButtonFactory buttonFactory, SpruceButtonWidget.PressAction action, @Nullable Text tooltip) { 32 | super(key); 33 | this.buttonFactory = buttonFactory; 34 | this.action = action; 35 | this.setTooltip(tooltip); 36 | } 37 | 38 | public SpruceSimpleActionOption(String key, ButtonFactory buttonFactory, SpruceButtonWidget.PressAction action) { 39 | this(key, buttonFactory, action, null); 40 | } 41 | 42 | @Override 43 | public SpruceWidget createWidget(Position position, int width) { 44 | var button = this.buttonFactory.build(position, width, Text.translatable(this.key), this.action); 45 | this.getOptionTooltip().ifPresent(button::setTooltip); 46 | return button; 47 | } 48 | 49 | public static SpruceSimpleActionOption of(String key, SpruceButtonWidget.PressAction action) { 50 | return new SpruceSimpleActionOption(key, 51 | (position, width, message, action1) -> new SpruceButtonWidget(position, width, 20, message, action1), 52 | action); 53 | } 54 | 55 | public static SpruceSimpleActionOption of(String key, SpruceButtonWidget.PressAction action, @Nullable Text tooltip) { 56 | return new SpruceSimpleActionOption(key, 57 | (position, width, message, action1) -> new SpruceButtonWidget(position, width, 20, message, action1), 58 | action, tooltip); 59 | } 60 | 61 | public static SpruceSimpleActionOption reset(SpruceButtonWidget.PressAction action) { 62 | return reset(action, null); 63 | } 64 | 65 | public static SpruceSimpleActionOption reset(SpruceButtonWidget.PressAction action, @Nullable Text tooltip) { 66 | return new SpruceSimpleActionOption("spruceui.reset", 67 | (position, width, message, action1) -> new SpruceButtonWidget(position, width, 20, message, action1), 68 | action, tooltip); 69 | } 70 | 71 | public static SpruceSimpleActionOption textured( 72 | String key, SpruceButtonWidget.PressAction action, 73 | int u, int v, int hoveredVOffset, Identifier texture 74 | ) { 75 | return textured(key, action, u, v, hoveredVOffset, texture, null); 76 | } 77 | 78 | public static SpruceSimpleActionOption textured( 79 | String key, SpruceButtonWidget.PressAction action, 80 | int u, int v, int hoveredVOffset, Identifier texture, @Nullable Text tooltip 81 | ) { 82 | return new SpruceSimpleActionOption(key, 83 | (position, width, message, action1) -> new SpruceTexturedButtonWidget(position, width, 20, message, action1, u, v, hoveredVOffset, texture), 84 | action, tooltip); 85 | } 86 | 87 | public static SpruceSimpleActionOption textured( 88 | String key, SpruceButtonWidget.PressAction action, 89 | int u, int v, int hoveredVOffset, Identifier texture, int textureWidth, int textureHeight 90 | ) { 91 | return textured(key, action, u, v, hoveredVOffset, texture, textureWidth, textureHeight, null); 92 | } 93 | 94 | public static SpruceSimpleActionOption textured( 95 | String key, SpruceButtonWidget.PressAction action, 96 | int u, int v, int hoveredVOffset, Identifier texture, int textureWidth, int textureHeight, @Nullable Text tooltip 97 | ) { 98 | return new SpruceSimpleActionOption(key, 99 | (position, width, message, action1) -> 100 | new SpruceTexturedButtonWidget(position, width, 20, message, action1, u, v, hoveredVOffset, texture, textureWidth, textureHeight), 101 | action, tooltip); 102 | } 103 | 104 | public static SpruceSimpleActionOption texturedWithMessage( 105 | String key, SpruceButtonWidget.PressAction action, 106 | int u, int v, int hoveredVOffset, Identifier texture, int textureWidth, int textureHeight 107 | ) { 108 | return texturedWithMessage(key, action, u, v, hoveredVOffset, texture, textureWidth, textureHeight, null); 109 | } 110 | 111 | public static SpruceSimpleActionOption texturedWithMessage( 112 | String key, SpruceButtonWidget.PressAction action, 113 | int u, int v, int hoveredVOffset, Identifier texture, int textureWidth, int textureHeight, @Nullable Text tooltip 114 | ) { 115 | return new SpruceSimpleActionOption(key, 116 | (position, width, message, action1) -> 117 | new SpruceTexturedButtonWidget(position, width, 20, message, true, action1, u, v, hoveredVOffset, texture, textureWidth, textureHeight), 118 | action, tooltip); 119 | } 120 | 121 | /** 122 | * Represents the button factory. 123 | * 124 | * @version 3.0.0 125 | * @since 2.0.0 126 | */ 127 | public interface ButtonFactory { 128 | SpruceButtonWidget build(Position position, int width, Text message, SpruceButtonWidget.PressAction action); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/option/SpruceStringOption.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.option; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 14 | import dev.lambdaurora.spruceui.widget.text.SpruceNamedTextFieldWidget; 15 | import dev.lambdaurora.spruceui.widget.text.SpruceTextFieldWidget; 16 | import net.minecraft.network.chat.Text; 17 | import org.jetbrains.annotations.Nullable; 18 | 19 | import java.util.function.Consumer; 20 | import java.util.function.Predicate; 21 | import java.util.function.Supplier; 22 | 23 | /** 24 | * Represents a string option. 25 | * 26 | * @author LambdAurora 27 | * @version 3.0.0 28 | * @since 2.1.0 29 | */ 30 | public class SpruceStringOption extends SpruceOption { 31 | private final Supplier getter; 32 | private final Consumer setter; 33 | private final @Nullable Predicate predicate; 34 | 35 | public SpruceStringOption(String key, Supplier getter, Consumer setter, @Nullable Predicate predicate, @Nullable Text tooltip) { 36 | super(key); 37 | this.getter = getter; 38 | this.setter = setter; 39 | this.predicate = predicate; 40 | this.setTooltip(tooltip); 41 | } 42 | 43 | @Override 44 | public SpruceWidget createWidget(Position position, int width) { 45 | var textField = new SpruceTextFieldWidget(position, width, 20, this.getPrefix()); 46 | textField.setText(this.get()); 47 | if (this.predicate != null) 48 | textField.setTextPredicate(this.predicate); 49 | textField.setChangedListener(this::set); 50 | this.getOptionTooltip().ifPresent(textField::setTooltip); 51 | return new SpruceNamedTextFieldWidget(textField); 52 | } 53 | 54 | public void set(String value) { 55 | this.setter.accept(value); 56 | } 57 | 58 | /** 59 | * Gets the current value. 60 | * 61 | * @return the current value 62 | */ 63 | public String get() { 64 | return this.getter.get(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/option/SpruceToggleBooleanOption.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.option; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.widget.SpruceToggleSwitch; 14 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 15 | import net.minecraft.network.chat.Text; 16 | import org.jetbrains.annotations.Nullable; 17 | 18 | import java.util.function.Consumer; 19 | import java.util.function.Supplier; 20 | 21 | /** 22 | * Represents a boolean option. 23 | *

24 | * Works the as {@link SpruceBooleanOption} but uses a toggle switch instead. 25 | * 26 | * @author LambdAurora 27 | * @version 6.1.0 28 | * @since 2.0.0 29 | */ 30 | public class SpruceToggleBooleanOption extends SpruceBooleanOption { 31 | private final boolean showMessage; 32 | 33 | public SpruceToggleBooleanOption(String key, Supplier getter, Consumer setter, @Nullable Text tooltip, boolean showMessage) { 34 | super(key, getter, setter, tooltip, false); 35 | this.showMessage = showMessage; 36 | } 37 | 38 | public SpruceToggleBooleanOption(String key, Supplier getter, Consumer setter, @Nullable Text tooltip) { 39 | this(key, getter, setter, tooltip, true); 40 | } 41 | 42 | @Override 43 | public SpruceWidget createWidget(Position position, int width) { 44 | var button = new SpruceToggleSwitch(position, width, 20, this.getDisplayText(), (btn, newValue) -> { 45 | this.set(); 46 | btn.setMessage(this.getDisplayText()); 47 | this.getOptionTooltip().ifPresent(btn::setTooltip); 48 | }, this.get(), this.showMessage); 49 | this.getOptionTooltip().ifPresent(button::setTooltip); 50 | return button; 51 | } 52 | 53 | @Override 54 | public Text getDisplayText() { 55 | return this.getPrefix(); 56 | } 57 | 58 | @Override 59 | public Text getDisplayText(Text value) { 60 | return this.getPrefix(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/screen/SpruceHandledScreen.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.screen; 11 | 12 | import dev.lambdaurora.spruceui.SprucePositioned; 13 | import dev.lambdaurora.spruceui.Tooltip; 14 | import dev.lambdaurora.spruceui.navigation.NavigationDirection; 15 | import dev.lambdaurora.spruceui.widget.SpruceElement; 16 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 17 | import net.minecraft.client.gui.GuiGraphics; 18 | import net.minecraft.client.gui.components.Renderable; 19 | import net.minecraft.client.gui.components.events.GuiEventListener; 20 | import net.minecraft.client.gui.screens.Screen; 21 | import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; 22 | import net.minecraft.network.chat.Text; 23 | import net.minecraft.world.entity.player.Inventory; 24 | import net.minecraft.world.inventory.AbstractContainerMenu; 25 | import org.lwjgl.glfw.GLFW; 26 | 27 | import java.util.function.BooleanSupplier; 28 | import java.util.function.Supplier; 29 | 30 | /** 31 | * Represents a handled screen. 32 | * 33 | * @param the type of the screen handler 34 | * @author LambdAurora 35 | * @version 5.0.0 36 | * @since 3.3.0 37 | */ 38 | public abstract class SpruceHandledScreen extends AbstractContainerScreen implements SprucePositioned, SpruceElement { 39 | public SpruceHandledScreen(T handler, Inventory inventory, Text title) { 40 | super(handler, inventory, title); 41 | } 42 | 43 | @Override 44 | public void setFocused(GuiEventListener focused) { 45 | var old = this.getFocused(); 46 | if (old == focused) return; 47 | if (old instanceof SpruceWidget) 48 | ((SpruceWidget) old).setFocused(false); 49 | super.setFocused(focused); 50 | if (focused instanceof SpruceWidget) 51 | ((SpruceWidget) focused).setFocused(true); 52 | } 53 | 54 | /* Input */ 55 | 56 | @Override 57 | public boolean keyPressed(int keyCode, int scanCode, int modifiers) { 58 | return super.keyPressed(keyCode, scanCode, modifiers) 59 | || NavigationDirection.fromKey(keyCode, Screen.hasShiftDown()) 60 | .map(dir -> this.onNavigation(dir, keyCode == GLFW.GLFW_KEY_TAB)) 61 | .orElseGet(() -> super.keyPressed(keyCode, scanCode, modifiers)); 62 | } 63 | 64 | /* Navigation */ 65 | 66 | @Override 67 | public boolean onNavigation(NavigationDirection direction, boolean tab) { 68 | if (this.requiresCursor()) return false; 69 | var focused = this.getFocused(); 70 | boolean isNonNull = focused != null; 71 | if (!isNonNull || !this.tryNavigating(focused, direction, tab)) { 72 | var children = this.children(); 73 | int i = children.indexOf(focused); 74 | int next; 75 | if (isNonNull && i >= 0) next = i + (direction.isLookingForward() ? 1 : 0); 76 | else if (direction.isLookingForward()) next = 0; 77 | else next = children.size(); 78 | 79 | var iterator = children.listIterator(next); 80 | BooleanSupplier hasNext = direction.isLookingForward() ? iterator::hasNext : iterator::hasPrevious; 81 | Supplier nextGetter = direction.isLookingForward() ? iterator::next : iterator::previous; 82 | 83 | GuiEventListener nextElement; 84 | do { 85 | if (!hasNext.getAsBoolean()) { 86 | this.setFocused(null); 87 | return false; 88 | } 89 | 90 | nextElement = nextGetter.get(); 91 | } while (!this.tryNavigating(nextElement, direction, tab)); 92 | 93 | this.setFocused(nextElement); 94 | } 95 | return true; 96 | } 97 | 98 | private boolean tryNavigating(GuiEventListener element, NavigationDirection direction, boolean tab) { 99 | if (element instanceof SpruceElement) { 100 | return ((SpruceElement) element).onNavigation(direction, tab); 101 | } 102 | element.setFocused(direction.isLookingForward()); 103 | return true; 104 | } 105 | 106 | /* Render */ 107 | 108 | @Override 109 | public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 110 | super.render(graphics, mouseX, mouseY, delta); 111 | this.renderWidgets(graphics, mouseX, mouseY, delta); 112 | this.renderTitle(graphics, mouseX, mouseY, delta); 113 | Tooltip.renderAll(graphics); 114 | } 115 | 116 | public void renderTitle(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 117 | } 118 | 119 | public void renderWidgets(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 120 | for (var element : this.children()) { 121 | if (element instanceof Renderable drawable) 122 | drawable.render(graphics, mouseX, mouseY, delta); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/screen/SpruceScreen.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.screen; 11 | 12 | import dev.lambdaurora.spruceui.SprucePositioned; 13 | import dev.lambdaurora.spruceui.Tooltip; 14 | import dev.lambdaurora.spruceui.navigation.NavigationDirection; 15 | import dev.lambdaurora.spruceui.widget.SpruceElement; 16 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 17 | import net.minecraft.client.gui.GuiGraphics; 18 | import net.minecraft.client.gui.components.Renderable; 19 | import net.minecraft.client.gui.components.events.GuiEventListener; 20 | import net.minecraft.client.gui.screens.Screen; 21 | import net.minecraft.network.chat.Text; 22 | import org.lwjgl.glfw.GLFW; 23 | 24 | import java.util.function.BooleanSupplier; 25 | import java.util.function.Supplier; 26 | 27 | /** 28 | * Represents a screen. 29 | * 30 | * @author LambdAurora 31 | * @version 3.3.0 32 | * @since 2.0.0 33 | */ 34 | public abstract class SpruceScreen extends Screen implements SprucePositioned, SpruceElement { 35 | protected SpruceScreen(Text title) { 36 | super(title); 37 | } 38 | 39 | @Override 40 | public void setFocused(GuiEventListener focused) { 41 | var old = this.getFocused(); 42 | if (old == focused) return; 43 | if (old instanceof SpruceWidget) 44 | old.setFocused(false); 45 | super.setFocused(focused); 46 | if (focused instanceof SpruceWidget) 47 | focused.setFocused(true); 48 | } 49 | 50 | /* Input */ 51 | 52 | @Override 53 | public boolean keyPressed(int keyCode, int scanCode, int modifiers) { 54 | return NavigationDirection.fromKey(keyCode, Screen.hasShiftDown()) 55 | .map(dir -> this.onNavigation(dir, keyCode == GLFW.GLFW_KEY_TAB)) 56 | .orElseGet(() -> super.keyPressed(keyCode, scanCode, modifiers)); 57 | } 58 | 59 | /* Navigation */ 60 | 61 | @Override 62 | public boolean onNavigation(NavigationDirection direction, boolean tab) { 63 | if (this.requiresCursor()) return false; 64 | var focused = this.getFocused(); 65 | boolean isNonNull = focused != null; 66 | if (!isNonNull || !this.tryNavigating(focused, direction, tab)) { 67 | var children = this.children(); 68 | int i = children.indexOf(focused); 69 | int next; 70 | if (isNonNull && i >= 0) next = i + (direction.isLookingForward() ? 1 : 0); 71 | else if (direction.isLookingForward()) next = 0; 72 | else next = children.size(); 73 | 74 | var iterator = children.listIterator(next); 75 | BooleanSupplier hasNext = direction.isLookingForward() ? iterator::hasNext : iterator::hasPrevious; 76 | Supplier nextGetter = direction.isLookingForward() ? iterator::next : iterator::previous; 77 | 78 | GuiEventListener nextElement; 79 | do { 80 | if (!hasNext.getAsBoolean()) { 81 | this.setFocused(null); 82 | return false; 83 | } 84 | 85 | nextElement = nextGetter.get(); 86 | } while (!this.tryNavigating(nextElement, direction, tab)); 87 | 88 | this.setFocused(nextElement); 89 | } 90 | return true; 91 | } 92 | 93 | private boolean tryNavigating(GuiEventListener element, NavigationDirection direction, boolean tab) { 94 | if (element instanceof SpruceElement) { 95 | return ((SpruceElement) element).onNavigation(direction, tab); 96 | } 97 | element.setFocused(direction.isLookingForward()); 98 | return true; 99 | } 100 | 101 | /* Render */ 102 | 103 | @Override 104 | public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 105 | this.renderBackground(graphics, mouseX, mouseY, delta); 106 | this.renderWidgets(graphics, mouseX, mouseY, delta); 107 | this.renderTitle(graphics, mouseX, mouseY, delta); 108 | Tooltip.renderAll(graphics); 109 | } 110 | 111 | public void renderTitle(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 112 | } 113 | 114 | public void renderWidgets(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 115 | for (var element : this.children()) { 116 | if (element instanceof Renderable drawable) 117 | drawable.render(graphics, mouseX, mouseY, delta); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/util/Identifiable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.util; 11 | 12 | import net.minecraft.resources.Identifier; 13 | 14 | /** 15 | * Represents something that can be identified. 16 | * 17 | * @author LambdAurora 18 | * @version 3.2.0 19 | * @since 3.2.0 20 | */ 21 | public interface Identifiable extends Nameable { 22 | /** 23 | * Gets the identifier of this object. 24 | * 25 | * @return the identifier of this object 26 | */ 27 | Identifier getIdentifier(); 28 | 29 | @Override 30 | default String getName() { 31 | return this.getIdentifier().path(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/util/Nameable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.util; 11 | 12 | /** 13 | * Represents something that can be named. 14 | * 15 | * @author LambdAurora 16 | * @version 3.2.0 17 | * @since 3.2.0 18 | */ 19 | public interface Nameable { 20 | /** 21 | * Gets the name of the object. 22 | * 23 | * @return the name of this object 24 | */ 25 | String getName(); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/util/RenderUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.util; 11 | 12 | import dev.lambdaurora.spruceui.SpruceTextures; 13 | import net.minecraft.client.gui.GuiGraphics; 14 | import net.minecraft.client.renderer.RenderType; 15 | 16 | public final class RenderUtil { 17 | private RenderUtil() { 18 | throw new IllegalStateException("RenderUtil only contains static-definitions."); 19 | } 20 | 21 | /** 22 | * Renders the dirt background texture. 23 | * 24 | * @param x the X coordinate 25 | * @param y the Y coordinate 26 | * @param width the width 27 | * @param height the height 28 | * @param vOffset the v offset 29 | * @see #renderBackgroundTexture(GuiGraphics, int, int, int, int, float, int, int, int, int) 30 | */ 31 | public static void renderBackgroundTexture( 32 | GuiGraphics graphics, int x, int y, int width, int height, float vOffset 33 | ) { 34 | renderBackgroundTexture(graphics, x, y, width, height, vOffset, 64, 64, 64, 255); 35 | } 36 | 37 | /** 38 | * Renders the dirt background texture. 39 | * 40 | * @param x the X-coordinate 41 | * @param y the Y-coordinate 42 | * @param width the width 43 | * @param height the height 44 | * @param vOffset the v offset 45 | * @param red the red-component color value 46 | * @param green the green-component color value 47 | * @param blue the blue-component color value 48 | * @param alpha the alpha-component alpha value 49 | */ 50 | public static void renderBackgroundTexture( 51 | GuiGraphics graphics, 52 | int x, int y, int width, int height, float vOffset, 53 | int red, int green, int blue, int alpha 54 | ) { 55 | graphics.drawSpecial(multiBufferSource -> { 56 | var buffer = multiBufferSource.getBuffer(RenderType.guiTextured(SpruceTextures.LEGACY_OPTIONS_BACKGROUND)); 57 | 58 | int right = x + width; 59 | int bottom = y + height; 60 | 61 | buffer.addVertex(x, bottom, 0) 62 | .uv(0, bottom / 32.f + vOffset) 63 | .color(red, green, blue, alpha); 64 | buffer.addVertex(right, bottom, 0) 65 | .uv(right / 32.f, bottom / 32.f + vOffset) 66 | .color(red, green, blue, alpha); 67 | buffer.addVertex(right, y, 0) 68 | .uv(right / 32.f, y / 32.f + vOffset) 69 | .color(red, green, blue, alpha); 70 | buffer.addVertex(x, y, 0) 71 | .uv(0, y / 32.f + vOffset) 72 | .color(red, green, blue, alpha); 73 | }); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/util/SpruceUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.util; 11 | 12 | /** 13 | * Provides some common utils. 14 | * 15 | * @author LambdAurora 16 | * @version 3.3.0 17 | * @since 3.2.1 18 | */ 19 | public class SpruceUtil { 20 | /** 21 | * Parses an integer from a string. If the value is not a integer it returns {@code 0}. 22 | * 23 | * @param value a {@code String} which represents an integer 24 | * @return the parsed integer, if parsing fails returns {@code 0}. 25 | */ 26 | public static int parseIntFromString(String value) { 27 | try { 28 | return Integer.parseInt(value); 29 | } catch (NumberFormatException e) { 30 | return 0; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/AbstractSpruceBooleanButtonWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import net.minecraft.network.chat.Text; 14 | 15 | /** 16 | * Represents a pressable button that switches between two states which values are {@code true} and {@code false}. 17 | * 18 | * @author LambdAurora 19 | * @version 3.3.0 20 | * @since 2.0.0 21 | */ 22 | public abstract class AbstractSpruceBooleanButtonWidget extends AbstractSprucePressableButtonWidget { 23 | private static final PressAction DEFAULT_ACTION = (button, newValue) -> { 24 | }; 25 | 26 | private final PressAction action; 27 | private boolean value; 28 | protected boolean showMessage; 29 | 30 | public AbstractSpruceBooleanButtonWidget(Position position, int width, int height, Text message, boolean value) { 31 | this(position, width, height, message, value, true); 32 | } 33 | 34 | public AbstractSpruceBooleanButtonWidget( 35 | Position position, int width, int height, Text message, boolean value, boolean showMessage 36 | ) { 37 | this(position, width, height, message, DEFAULT_ACTION, value, showMessage); 38 | } 39 | 40 | public AbstractSpruceBooleanButtonWidget( 41 | Position position, int width, int height, Text message, PressAction action, boolean value 42 | ) { 43 | this(position, width, height, message, action, value, true); 44 | } 45 | 46 | public AbstractSpruceBooleanButtonWidget( 47 | Position position, int width, int height, Text message, PressAction action, boolean value, boolean showMessage 48 | ) { 49 | super(position, width, height, message); 50 | this.action = action; 51 | this.value = value; 52 | this.showMessage = showMessage; 53 | } 54 | 55 | /** 56 | * Returns the value of this button. 57 | * 58 | * @return {@code true} or {@code false}. 59 | */ 60 | public boolean getValue() { 61 | return this.value; 62 | } 63 | 64 | @Override 65 | public void onPress() { 66 | this.value = !this.value; 67 | this.action.onPress(this, this.value); 68 | } 69 | 70 | /** 71 | * Represents the press action handler. 72 | * 73 | * @version 3.0.0 74 | * @since 2.0.0 75 | */ 76 | public interface PressAction { 77 | void onPress(AbstractSpruceBooleanButtonWidget button, boolean newValue); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/AbstractSpruceIconButtonWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import net.minecraft.client.gui.GuiGraphics; 14 | import net.minecraft.network.chat.Text; 15 | import net.minecraft.util.math.MathHelper; 16 | 17 | public abstract class AbstractSpruceIconButtonWidget extends SpruceButtonWidget { 18 | public AbstractSpruceIconButtonWidget(Position position, int width, int height, Text message, PressAction action) { 19 | super(position, width, height, message, action); 20 | } 21 | 22 | /** 23 | * Renders the icon of the button. 24 | * 25 | * @return the x-offset the icon creates 26 | */ 27 | protected abstract int renderIcon(GuiGraphics graphics, int mouseX, int mouseY, float delta); 28 | 29 | @Override 30 | protected void renderButton(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 31 | int iconWidth = this.renderIcon(graphics, mouseX, mouseY, delta); 32 | if (!this.getMessage().getString().isEmpty()) { 33 | int color = this.isActive() ? 16777215 : 10526880; 34 | graphics.drawCenteredShadowedText(this.client.font, this.getMessage(), 35 | this.getX() + 8 + iconWidth + (this.getWidth() - 8 - iconWidth - 6) / 2, 36 | this.getY() + (this.height - 8) / 2, color | MathHelper.ceil(this.getAlpha() * 255.0F) << 24); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/AbstractSprucePressableButtonWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import net.minecraft.network.chat.Text; 14 | import org.lwjgl.glfw.GLFW; 15 | 16 | /** 17 | * Represents a pressable button widget. 18 | * 19 | * @author LambdAurora 20 | * @version 3.3.0 21 | * @since 2.0.0 22 | */ 23 | public abstract class AbstractSprucePressableButtonWidget extends AbstractSpruceButtonWidget { 24 | public AbstractSprucePressableButtonWidget(Position position, int width, int height, Text message) { 25 | super(position, width, height, message); 26 | } 27 | 28 | public abstract void onPress(); 29 | 30 | @Override 31 | public void onClick(double mouseX, double mouseY) { 32 | this.onPress(); 33 | this.playDownSound(); 34 | } 35 | 36 | @Override 37 | protected boolean onKeyPress(int keyCode, int scanCode, int modifiers) { 38 | if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER || keyCode == GLFW.GLFW_KEY_SPACE) { 39 | this.onPress(); 40 | this.playDownSound(); 41 | return true; 42 | } 43 | return false; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/SpruceButtonWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import net.minecraft.network.chat.Text; 14 | 15 | /** 16 | * Represents a button widget. 17 | * 18 | * @author LambdAurora 19 | * @version 3.0.0 20 | * @since 1.0.0 21 | */ 22 | public class SpruceButtonWidget extends AbstractSprucePressableButtonWidget { 23 | private final PressAction action; 24 | 25 | public SpruceButtonWidget(Position position, int width, int height, Text message, PressAction action) { 26 | super(position, width, height, message); 27 | this.action = action; 28 | } 29 | 30 | @Override 31 | public void onPress() { 32 | this.action.onPress(this); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return this.getClass().getSimpleName() + '{' + 38 | "position=" + this.getPosition() + 39 | ", width=" + this.getWidth() + 40 | ", height=" + this.getHeight() + 41 | ", visible=" + this.isVisible() + 42 | ", active=" + this.isActive() + 43 | ", message=" + this.getMessage() + 44 | ", focused=" + this.isFocused() + 45 | ", hovered=" + this.isMouseHovered() + 46 | ", wasHovered=" + this.wasHovered + 47 | ", dragging=" + this.dragging + 48 | ", lastDrag=" + this.lastDrag + 49 | ", alpha=" + this.getAlpha() + 50 | '}'; 51 | } 52 | 53 | public interface PressAction { 54 | void onPress(SpruceButtonWidget button); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/SpruceCheckboxWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.SpruceUI; 14 | import net.minecraft.client.gui.GuiGraphics; 15 | import net.minecraft.client.gui.components.WidgetSprites; 16 | import net.minecraft.client.renderer.RenderType; 17 | import net.minecraft.locale.Language; 18 | import net.minecraft.network.chat.Text; 19 | import net.minecraft.resources.Identifier; 20 | import net.minecraft.util.math.MathHelper; 21 | 22 | /** 23 | * Represents a checkbox widget. 24 | * 25 | * @author LambdAurora 26 | * @version 6.0.0 27 | * @since 1.0.0 28 | */ 29 | public class SpruceCheckboxWidget extends AbstractSpruceBooleanButtonWidget { 30 | public static final WidgetSprites BACKGROUND_TEXTURE = new WidgetSprites( 31 | Identifier.ofDefault("widget/checkbox"), 32 | Identifier.ofDefault("widget/checkbox_highlighted") 33 | ); 34 | public static final Identifier CHECKED_TEXTURE = SpruceUI.id("widget/checkbox/checked"); 35 | public static final Identifier CROSSED_TEXTURE = SpruceUI.id("widget/checkbox/crossed"); 36 | private boolean showCross = false; 37 | private boolean colored = false; 38 | 39 | public SpruceCheckboxWidget(Position position, int width, int height, Text message, boolean value) { 40 | super(position, width, height, message, value); 41 | } 42 | 43 | public SpruceCheckboxWidget(Position position, int width, int height, Text message, boolean value, boolean showMessage) { 44 | super(position, width, height, message, value, showMessage); 45 | } 46 | 47 | public SpruceCheckboxWidget(Position position, int width, int height, Text message, PressAction action, boolean value) { 48 | super(position, width, height, message, action, value); 49 | } 50 | 51 | public SpruceCheckboxWidget(Position position, int width, int height, Text message, PressAction action, boolean value, boolean showMessage) { 52 | super(position, width, height, message, action, value, showMessage); 53 | } 54 | 55 | /** 56 | * Returns whether this checkbox shows a cross for the {@code false} value. 57 | * 58 | * @return {@code true} if this checkbox can show a cross, else {@code false} 59 | */ 60 | public boolean showCross() { 61 | return this.showCross; 62 | } 63 | 64 | /** 65 | * Sets whether this checkbox shows a cross for the {@code false} value. 66 | * 67 | * @param showCross {@code true} if this checkbox can show a cross, else {@code false} 68 | */ 69 | public void setShowCross(boolean showCross) { 70 | this.showCross = showCross; 71 | } 72 | 73 | /** 74 | * Returns whether this checkbox is colored or not. 75 | * 76 | * @return {@code true} if colored, else {@code false} 77 | */ 78 | public boolean isColored() { 79 | return this.colored; 80 | } 81 | 82 | /** 83 | * Sets whether this checkbox is colored or not. 84 | * 85 | * @param colored {@code true} if colored, else {@code false} 86 | */ 87 | public void setColored(boolean colored) { 88 | this.colored = colored; 89 | } 90 | 91 | /* Rendering */ 92 | 93 | @Override 94 | protected void renderButton(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 95 | if (this.getValue()) { 96 | graphics.drawSprite( 97 | RenderType::guiTextured, CHECKED_TEXTURE, 98 | this.getX(), this.getY(), 99 | this.getHeight(), this.getHeight(), 100 | this.colored ? 0xff00ff00 : -1 101 | ); 102 | } else if (this.showCross) { 103 | graphics.drawSprite( 104 | RenderType::guiTextured, CROSSED_TEXTURE, 105 | this.getX(), this.getY(), 106 | this.getHeight(), this.getHeight(), 107 | this.colored ? 0xffff0000 : -1 108 | ); 109 | } 110 | 111 | if (this.showMessage) { 112 | var message = Language.getInstance().getVisualOrder(this.client.font.substrByWidth(this.getMessage(), this.getWidth() - this.getHeight() - 4)); 113 | graphics.drawShadowedText(this.client.font, message, this.getX() + this.getHeight() + 4, this.getY() + (this.getHeight() - 8) / 2, 114 | 14737632 | MathHelper.ceil(this.alpha * 255.0F) << 24); 115 | } 116 | } 117 | 118 | @Override 119 | protected void renderBackground(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 120 | graphics.drawSprite( 121 | RenderType::guiTextured, BACKGROUND_TEXTURE.get(this.isActive(), this.isFocusedOrHovered()), 122 | this.getX(), this.getY(), 123 | this.getHeight(), this.getHeight() 124 | ); 125 | } 126 | 127 | /* Narration */ 128 | 129 | @Override 130 | protected Text getNarrationFocusedUsageMessage() { 131 | return Text.translatable("narration.checkbox.usage.focused"); 132 | } 133 | 134 | @Override 135 | protected Text getNarrationHoveredUsageMessage() { 136 | return Text.translatable("narration.checkbox.usage.hovered"); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/SpruceElement.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget; 11 | 12 | import dev.lambdaurora.spruceui.navigation.NavigationDirection; 13 | import net.minecraft.client.gui.components.events.GuiEventListener; 14 | 15 | /** 16 | * Represents an element with navigation and controller input implementation. 17 | * 18 | * @author LambdAurora 19 | * @version 3.0.0 20 | * @since 2.0.0 21 | */ 22 | public interface SpruceElement extends GuiEventListener { 23 | /** 24 | * Called when navigating in the menu. 25 | * 26 | * @param direction direction of navigation 27 | * @param tab {@code true} if the navigation was triggered by the tab key, else {@code false} 28 | * @return {@code true} if success, else {@code false} 29 | */ 30 | default boolean onNavigation(NavigationDirection direction, boolean tab) { 31 | if (this.requiresCursor()) return false; 32 | if (direction.isVertical()) { 33 | this.setFocused(direction == NavigationDirection.DOWN); 34 | return true; 35 | } 36 | return false; 37 | } 38 | 39 | /** 40 | * Returns whether this is element requires a cursor to be used. 41 | * 42 | * @return {@code true} if a cursor is required, else {@code false} 43 | */ 44 | default boolean requiresCursor() { 45 | return false; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/SpruceIconButtonWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import net.minecraft.client.gui.GuiGraphics; 14 | import net.minecraft.network.chat.Text; 15 | 16 | public class SpruceIconButtonWidget extends AbstractSpruceIconButtonWidget { 17 | public SpruceIconButtonWidget(Position position, int width, int height, Text message, PressAction action) { 18 | super(position, width, height, message, action); 19 | } 20 | 21 | /** 22 | * Renders the icon of the button. 23 | * 24 | * @return the x-offset the icon creates 25 | */ 26 | protected int renderIcon(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 27 | return 0; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/SpruceSeparatorWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.Tooltip; 14 | import dev.lambdaurora.spruceui.Tooltipable; 15 | import dev.lambdaurora.spruceui.util.ColorUtil; 16 | import net.minecraft.client.gui.GuiGraphics; 17 | import net.minecraft.network.chat.Text; 18 | import net.minecraft.util.FormattedCharSequence; 19 | import org.jetbrains.annotations.Nullable; 20 | 21 | import java.util.List; 22 | import java.util.Optional; 23 | 24 | /** 25 | * Represents a separator element. 26 | * 27 | * @author LambdAurora 28 | * @version 5.0.0 29 | * @since 1.0.1 30 | */ 31 | public class SpruceSeparatorWidget extends AbstractSpruceWidget implements Tooltipable { 32 | private Text title; 33 | private List titleToRender = List.of(); 34 | private Text tooltip; 35 | private int tooltipTicks; 36 | private long lastTick; 37 | 38 | public SpruceSeparatorWidget(Position position, int width, @Nullable Text title) { 39 | super(position); 40 | this.width = width; 41 | this.setTitle(title); 42 | } 43 | 44 | @Deprecated 45 | public SpruceSeparatorWidget(@Nullable Text title, int x, int y, int width) { 46 | this(Position.of(x, y), width, title); 47 | } 48 | 49 | /** 50 | * Gets the title of this separator widget. 51 | * 52 | * @return the title 53 | */ 54 | public Optional getTitle() { 55 | return Optional.ofNullable(this.title); 56 | } 57 | 58 | protected int getTitleWidth() { 59 | if (this.titleToRender.isEmpty()) { 60 | return 0; 61 | } 62 | 63 | int max = this.getWidth() - 8; 64 | int width = 0; 65 | 66 | for (var line : this.titleToRender) { 67 | width = Math.max(width, this.client.font.width(line)); 68 | } 69 | 70 | return Math.min(width, max); 71 | } 72 | 73 | /** 74 | * Sets the title of this separator widget. 75 | * 76 | * @param title the title 77 | */ 78 | public void setTitle(@Nullable Text title) { 79 | this.title = title; 80 | 81 | if (this.title != null) { 82 | this.titleToRender = this.client.font.wrapLines(this.title, this.getWidth() - 8); 83 | } else { 84 | this.titleToRender = List.of(); 85 | } 86 | 87 | this.height = this.client.font.lineHeight; 88 | 89 | for (int i = 1; i < this.titleToRender.size(); i++) { 90 | this.height += 2 + this.client.font.lineHeight; 91 | } 92 | } 93 | 94 | @Override 95 | public Optional getTooltip() { 96 | return Optional.ofNullable(this.tooltip); 97 | } 98 | 99 | @Override 100 | public void setTooltip(@Nullable Text tooltip) { 101 | this.tooltip = tooltip; 102 | } 103 | 104 | /* Navigation */ 105 | 106 | @Override 107 | public boolean requiresCursor() { 108 | return this.tooltip == null; 109 | } 110 | 111 | /* Rendering */ 112 | 113 | @Override 114 | protected void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 115 | int lineY = this.getY() + this.getHeight() / 2 - 1; 116 | 117 | if (this.title != null) { 118 | int titleWidth = this.getTitleWidth(); 119 | int titleX = this.getX() + (this.getWidth() / 2 - titleWidth / 2); 120 | graphics.fill(this.getX(), lineY, titleX - 5, lineY + 2, ColorUtil.TEXT_COLOR); 121 | graphics.fill(titleX + titleWidth + 5, lineY, this.getX() + this.getWidth(), lineY + 2, ColorUtil.TEXT_COLOR); 122 | 123 | int y = this.getY(); 124 | for (var line : this.titleToRender) { 125 | int lineX = this.getX() + (this.getWidth() / 2 - this.client.font.width(line) / 2); 126 | graphics.drawShadowedText(this.client.font, line, lineX, y, ColorUtil.WHITE); 127 | y += 2 + this.client.font.lineHeight; 128 | } 129 | } else { 130 | graphics.fill(this.getX(), lineY, this.getX() + this.getWidth(), lineY + 2, ColorUtil.TEXT_COLOR); 131 | } 132 | 133 | Tooltip.queueFor(this, mouseX, mouseY, this.tooltipTicks, i -> this.tooltipTicks = i, this.lastTick, i -> this.lastTick = i); 134 | } 135 | 136 | /* Narration */ 137 | 138 | @Override 139 | protected Text getNarrationMessage() { 140 | return this.getTitle().map(Text::getString) 141 | .filter(title -> !title.isEmpty()) 142 | .map(title -> Text.translatable("spruceui.narrator.separator", title)) 143 | .orElse(null); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/SpruceTexturedButtonWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import net.minecraft.client.gui.GuiGraphics; 14 | import net.minecraft.client.renderer.RenderType; 15 | import net.minecraft.network.chat.Text; 16 | import net.minecraft.resources.Identifier; 17 | 18 | /** 19 | * Represents a textured button widget. 20 | * 21 | * @author LambdAurora 22 | * @version 5.0.0 23 | * @since 2.0.0 24 | */ 25 | public class SpruceTexturedButtonWidget extends SpruceButtonWidget { 26 | private final Identifier texture; 27 | private final int u; 28 | private final int v; 29 | private final int hoveredVOffset; 30 | private final int textureWidth; 31 | private final int textureHeight; 32 | private final boolean showMessage; 33 | 34 | public SpruceTexturedButtonWidget(Position position, int width, int height, Text message, PressAction action, int u, int v, 35 | int hoveredVOffset, Identifier texture) { 36 | this(position, width, height, message, false, action, u, v, hoveredVOffset, texture); 37 | } 38 | 39 | public SpruceTexturedButtonWidget(Position position, int width, int height, Text message, boolean showMessage, PressAction action, 40 | int u, int v, int hoveredVOffset, Identifier texture) { 41 | this(position, width, height, message, showMessage, action, u, v, hoveredVOffset, texture, 256, 256); 42 | } 43 | 44 | public SpruceTexturedButtonWidget(Position position, int width, int height, Text message, PressAction action, 45 | int u, int v, int hoveredVOffset, Identifier texture, int textureWidth, int textureHeight) { 46 | this(position, width, height, message, false, action, u, v, hoveredVOffset, texture, textureWidth, textureHeight); 47 | } 48 | 49 | public SpruceTexturedButtonWidget(Position position, int width, int height, Text message, boolean showMessage, PressAction action, 50 | int u, int v, int hoveredVOffset, Identifier texture, int textureWidth, int textureHeight) { 51 | super(position, width, height, message, action); 52 | this.texture = texture; 53 | this.u = u; 54 | this.v = v; 55 | this.hoveredVOffset = hoveredVOffset; 56 | this.textureWidth = textureWidth; 57 | this.textureHeight = textureHeight; 58 | this.showMessage = showMessage; 59 | } 60 | 61 | /* Rendering */ 62 | 63 | @Override 64 | protected void renderButton(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 65 | if (this.showMessage) 66 | super.renderButton(graphics, mouseX, mouseY, delta); 67 | } 68 | 69 | @Override 70 | protected void renderBackground(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 71 | int v = this.v; 72 | if (this.isFocusedOrHovered()) { 73 | v += this.hoveredVOffset; 74 | } 75 | 76 | graphics.drawTexture( 77 | RenderType::guiTextured, this.texture, 78 | this.getX(), this.getY(), 79 | this.u, v, 80 | this.getWidth(), this.getHeight(), 81 | this.textureWidth, this.textureHeight 82 | ); 83 | } 84 | 85 | @Override 86 | public String toString() { 87 | return "SpruceTexturedButtonWidget{" + 88 | "position=" + this.getPosition() + 89 | ", width=" + this.getWidth() + 90 | ", height=" + this.getHeight() + 91 | ", visible=" + this.isVisible() + 92 | ", active=" + this.isActive() + 93 | ", message=" + this.getMessage() + 94 | ", focused=" + this.isFocused() + 95 | ", hovered=" + this.isMouseHovered() + 96 | ", wasHovered=" + this.wasHovered + 97 | ", dragging=" + this.dragging + 98 | ", lastDrag=" + this.lastDrag + 99 | ", alpha=" + this.getAlpha() + 100 | ", texture=" + this.texture + 101 | ", u=" + this.u + 102 | ", v=" + this.v + 103 | ", hoveredVOffset=" + this.hoveredVOffset + 104 | ", textureWidth=" + this.textureWidth + 105 | ", textureHeight=" + this.textureHeight + 106 | ", showMessage=" + this.showMessage + 107 | '}'; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/SpruceToggleSwitch.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.SpruceTexts; 14 | import dev.lambdaurora.spruceui.SpruceUI; 15 | import net.minecraft.client.gui.GuiGraphics; 16 | import net.minecraft.client.gui.components.WidgetSprites; 17 | import net.minecraft.client.renderer.RenderType; 18 | import net.minecraft.locale.Language; 19 | import net.minecraft.network.chat.Text; 20 | import net.minecraft.util.math.MathHelper; 21 | import org.jetbrains.annotations.Nullable; 22 | 23 | /** 24 | * Represents a checkbox widget. 25 | * 26 | * @author LambdAurora 27 | * @version 6.0.0 28 | * @since 1.0.0 29 | */ 30 | public class SpruceToggleSwitch extends AbstractSpruceBooleanButtonWidget { 31 | public static final WidgetSprites BACKGROUND_TEXTURE = new WidgetSprites( 32 | SpruceUI.id("widget/toggle_switch/background"), 33 | SpruceUI.id("widget/toggle_switch/background_highlighted") 34 | ); 35 | public static final WidgetSprites ON_TEXTURE = new WidgetSprites( 36 | SpruceUI.id("widget/toggle_switch/on"), 37 | SpruceUI.id("widget/toggle_switch/on_highlighted") 38 | ); 39 | public static final WidgetSprites OFF_TEXTURE = new WidgetSprites( 40 | SpruceUI.id("widget/toggle_switch/off"), 41 | SpruceUI.id("widget/toggle_switch/off_highlighted") 42 | ); 43 | 44 | public SpruceToggleSwitch(Position position, int width, int height, Text message, boolean value) { 45 | super(position, width, height, message, value); 46 | } 47 | 48 | public SpruceToggleSwitch(Position position, int width, int height, Text message, boolean value, 49 | boolean showMessage) { 50 | super(position, width, height, message, value, showMessage); 51 | } 52 | 53 | public SpruceToggleSwitch(Position position, int width, int height, Text message, PressAction action, 54 | boolean value) { 55 | super(position, width, height, message, action, value); 56 | } 57 | 58 | public SpruceToggleSwitch(Position position, int width, int height, Text message, PressAction action, 59 | boolean value, boolean showMessage) { 60 | super(position, width, height, message, action, value, showMessage); 61 | } 62 | 63 | /* Rendering */ 64 | 65 | @Override 66 | protected void renderButton(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 67 | graphics.drawSprite( 68 | RenderType::guiTextured, (this.getValue() ? ON_TEXTURE : OFF_TEXTURE).get(this.isActive(), this.isFocusedOrHovered()), 69 | this.getX() + (this.getValue() ? 14 : 0), this.getY() + (this.getHeight() / 2 - 9), 70 | 18, 18 71 | ); 72 | 73 | if (this.showMessage) { 74 | var message = Language.getInstance().getVisualOrder( 75 | this.client.font.substrByWidth(this.getMessage(), this.getWidth() - 40) 76 | ); 77 | graphics.drawShadowedText(this.client.font, message, this.getX() + 36, this.getY() + (this.getHeight() - 8) / 2, 78 | 14737632 | MathHelper.ceil(this.alpha * 255.0F) << 24); 79 | } 80 | } 81 | 82 | @Override 83 | protected void renderBackground(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 84 | graphics.drawSprite( 85 | RenderType::guiTextured, BACKGROUND_TEXTURE.get(this.isActive(), this.isFocusedOrHovered()), 86 | this.getX(), this.getY() + (this.getHeight() / 2 - 9), 87 | 32, 18 88 | ); 89 | } 90 | 91 | /* Narration */ 92 | 93 | @Override 94 | protected @Nullable Text getNarrationMessage() { 95 | return Text.translatable("spruceui.narration.toggle_switch", this.getMessage(), 96 | SpruceTexts.getToggleText(this.getValue())); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/SpruceWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.SprucePositioned; 14 | import net.minecraft.client.gui.components.Renderable; 15 | import net.minecraft.client.gui.narration.NarratableEntry; 16 | 17 | /** 18 | * Represents a widget. 19 | * 20 | * @author LambdAurora 21 | * @version 3.3.0 22 | * @since 1.6.0 23 | */ 24 | public interface SpruceWidget extends SprucePositioned, SpruceElement, NarratableEntry, Renderable { 25 | /** 26 | * Returns the position of the widget. 27 | * 28 | * @return the position 29 | */ 30 | Position getPosition(); 31 | 32 | @Override 33 | default int getX() { 34 | return this.getPosition().getX(); 35 | } 36 | 37 | @Override 38 | default int getY() { 39 | return this.getPosition().getY(); 40 | } 41 | 42 | /** 43 | * {@return the X coordinate of the end of this widget} 44 | */ 45 | default int getEndX() { 46 | return this.getX() + this.getWidth(); 47 | } 48 | 49 | /** 50 | * {@return the Y coordinate of the end of this widget} 51 | */ 52 | default int getEndY() { 53 | return this.getY() + this.getHeight(); 54 | } 55 | 56 | /** 57 | * Returns the widget width. 58 | * 59 | * @return the width 60 | */ 61 | int getWidth(); 62 | 63 | /** 64 | * Returns the widget height. 65 | * 66 | * @return the height 67 | */ 68 | int getHeight(); 69 | 70 | /** 71 | * Returns whether the widget is visible or not. 72 | * 73 | * @return {@code true} if the widget is visible, else {@code false} 74 | */ 75 | boolean isVisible(); 76 | 77 | /** 78 | * Sets whether the widget is visible or not. 79 | * 80 | * @param visible {@code true} if the widget is visible, else {@code false} 81 | */ 82 | void setVisible(boolean visible); 83 | 84 | /** 85 | * Returns whether this widget is active or not. 86 | * 87 | * @return {@code true} if the widget is active, else {@code false} 88 | */ 89 | boolean isActive(); 90 | 91 | /** 92 | * Sets whether this widget is active or not. 93 | * 94 | * @param active {@code true} if the widget is active, else {@code false} 95 | */ 96 | void setActive(boolean active); 97 | 98 | /** 99 | * Returns whether the widget is focused or not. 100 | * 101 | * @return {@code true} if the widget is focused, else {@code false} 102 | */ 103 | boolean isFocused(); 104 | 105 | /** 106 | * Sets whether the widget is focused or not. 107 | * 108 | * @param focused {@code true} if the widget is focused, else {@code false} 109 | */ 110 | void setFocused(boolean focused); 111 | 112 | /** 113 | * Returns whether the widget is hovered or not. 114 | * 115 | * @return {@code true} if the widget is hovered, else {@code false} 116 | */ 117 | default boolean isMouseHovered() { 118 | return this.narrationPriority() == NarrationPriority.HOVERED; 119 | } 120 | 121 | /** 122 | * Returns whether the widget is focused or hovered. 123 | * 124 | * @return {@code true} if the widget is focused or hovered, else {@code false} 125 | */ 126 | default boolean isFocusedOrHovered() { 127 | return this.isMouseHovered() || this.isFocused(); 128 | } 129 | 130 | @Override 131 | default boolean isMouseOver(double mouseX, double mouseY) { 132 | return this.isVisible() && mouseX >= (double) this.getX() && mouseX < (double) (this.getX() + this.getWidth()) && mouseY >= (double) this.getY() && mouseY < (double) (this.getY() + this.getHeight()); 133 | } 134 | 135 | boolean isDragging(); 136 | 137 | void setDragging(boolean dragging); 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/SpruceWidgetWithBorder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2024 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget; 11 | 12 | /** 13 | * Represents a widget that has a border. 14 | * 15 | * @author LambdAurora 16 | * @version 5.1.0 17 | * @since 5.1.0 18 | */ 19 | public interface SpruceWidgetWithBorder extends SpruceWidget, WithBorder { 20 | /** 21 | * {@return the inner X-coordinate of the widget depending on its border} 22 | */ 23 | default int getInnerBorderedX() { 24 | return this.getX() + this.getBorder().getLeft(); 25 | } 26 | 27 | /** 28 | * {@return the inner Y-coordinate of the widget depending on its border} 29 | */ 30 | default int getInnerBorderedY() { 31 | return this.getY() + this.getBorder().getTop(); 32 | } 33 | 34 | /** 35 | * {@return the inner end X-coordinate of the widget depending on its border} 36 | */ 37 | default int getEndInnerBorderedX() { 38 | return this.getEndX() - this.getBorder().getRight(); 39 | } 40 | 41 | /** 42 | * {@return the inner end Y-coordinate of the widget depending on its border} 43 | */ 44 | default int getEndInnerBorderedY() { 45 | return this.getEndY() - this.getBorder().getBottom(); 46 | } 47 | 48 | /** 49 | * {@return the inner width of the widget depending on its border} 50 | */ 51 | default int getInnerBorderedWidth() { 52 | return this.getWidth() - (this.getBorder().getLeft() + this.getBorder().getRight()); 53 | } 54 | 55 | /** 56 | * {@return the inner height of the widget depending on its border} 57 | */ 58 | default int getInnerBorderedHeight() { 59 | return this.getHeight() - (this.getBorder().getTop() + this.getBorder().getBottom()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/WithBackground.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget; 11 | 12 | import dev.lambdaurora.spruceui.background.Background; 13 | 14 | /** 15 | * Represents a widget with a background. 16 | * 17 | * @author LambdAurora 18 | * @version 3.3.0 19 | * @since 2.0.0 20 | */ 21 | public interface WithBackground { 22 | /** 23 | * Returns the background of this widget. 24 | * 25 | * @return the background 26 | */ 27 | Background getBackground(); 28 | 29 | /** 30 | * Sets the background of this widget. 31 | * 32 | * @param background the background 33 | */ 34 | void setBackground(Background background); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/WithBorder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget; 11 | 12 | import dev.lambdaurora.spruceui.border.Border; 13 | import dev.lambdaurora.spruceui.border.EmptyBorder; 14 | 15 | /** 16 | * Represents a widget with a border. 17 | * 18 | * @author LambdAurora 19 | * @version 3.3.0 20 | * @since 2.0.0 21 | */ 22 | public interface WithBorder { 23 | /** 24 | * Returns whether this widget has a border or not. 25 | * 26 | * @return {@code true} if this widget has a border, else {@code false}. 27 | */ 28 | default boolean hasBorder() { 29 | return this.getBorder().getThickness() != 0 && this.getBorder() != EmptyBorder.EMPTY_BORDER; 30 | } 31 | 32 | /** 33 | * Gets the border of this widget. 34 | * 35 | * @return the border 36 | */ 37 | Border getBorder(); 38 | 39 | /** 40 | * Sets the border of this widget. 41 | * 42 | * @param border the border 43 | */ 44 | void setBorder(Border border); 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/container/AbstractSpruceParentWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget.container; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.navigation.NavigationDirection; 14 | import dev.lambdaurora.spruceui.navigation.NavigationUtils; 15 | import dev.lambdaurora.spruceui.widget.AbstractSpruceWidget; 16 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 17 | import org.jetbrains.annotations.Nullable; 18 | import org.lwjgl.glfw.GLFW; 19 | 20 | /** 21 | * Represents a parent widget, contains children which are other widgets. 22 | * 23 | * @param the type of children widgets 24 | * @author LambdAurora 25 | * @version 3.3.0 26 | * @since 2.0.0 27 | */ 28 | public abstract class AbstractSpruceParentWidget extends AbstractSpruceWidget implements SpruceParentWidget { 29 | private final Class childClass; 30 | private @Nullable E focused; 31 | 32 | public AbstractSpruceParentWidget(Position position, Class childClass) { 33 | super(position); 34 | this.childClass = childClass; 35 | } 36 | 37 | @Override 38 | public void setFocused(boolean focused) { 39 | super.setFocused(focused); 40 | if (!focused) { 41 | this.setFocused(null); 42 | } 43 | } 44 | 45 | @Override 46 | public @Nullable E getFocused() { 47 | return this.focused; 48 | } 49 | 50 | @Override 51 | public void setFocused(@Nullable E focused) { 52 | if (this.focused == focused) 53 | return; 54 | if (this.focused != null) 55 | this.focused.setFocused(false); 56 | if (focused == null) 57 | this.focused = null; 58 | else if (this.childClass.isInstance(focused)) { 59 | this.focused = focused; 60 | this.focused.setFocused(true); 61 | } 62 | } 63 | 64 | protected void setOwnerShip(E child) { 65 | child.getPosition().setAnchor(this); 66 | } 67 | 68 | /* Navigation */ 69 | 70 | @Override 71 | public boolean onNavigation(NavigationDirection direction, boolean tab) { 72 | if (this.requiresCursor()) return false; 73 | boolean result = NavigationUtils.tryNavigate(direction, tab, this.children(), this.focused, this::setFocused, false); 74 | if (result) 75 | this.setFocused(true); 76 | return result; 77 | } 78 | 79 | /* Input */ 80 | 81 | @Override 82 | protected boolean onMouseClick(double mouseX, double mouseY, int button) { 83 | var it = this.iterator(); 84 | 85 | E element; 86 | do { 87 | if (!it.hasNext()) { 88 | return false; 89 | } 90 | 91 | element = it.next(); 92 | } while (!element.mouseClicked(mouseX, mouseY, button)); 93 | 94 | this.setFocused(element); 95 | if (button == GLFW.GLFW_MOUSE_BUTTON_1) { 96 | this.setDragging(true); 97 | } 98 | 99 | return true; 100 | } 101 | 102 | @Override 103 | protected boolean onMouseRelease(double mouseX, double mouseY, int button) { 104 | this.setDragging(false); 105 | return this.hoveredElement(mouseX, mouseY).filter(element -> element.mouseReleased(mouseX, mouseY, button)).isPresent(); 106 | } 107 | 108 | @Override 109 | protected boolean onMouseDrag(double mouseX, double mouseY, int button, double deltaX, double deltaY) { 110 | return this.getFocused() != null && this.isDragging() && button == GLFW.GLFW_MOUSE_BUTTON_1 111 | && this.getFocused().mouseDragged(mouseX, mouseY, button, deltaX, deltaY); 112 | } 113 | 114 | @Override 115 | protected boolean onMouseScroll(double mouseX, double mouseY, double scrollX, double scrollY) { 116 | return this.hoveredElement(mouseX, mouseY).filter(element -> element.mouseScrolled(mouseX, mouseY, scrollX, scrollY)).isPresent(); 117 | } 118 | 119 | @Override 120 | protected boolean onKeyPress(int keyCode, int scanCode, int modifiers) { 121 | return this.getFocused() != null && this.getFocused().keyPressed(keyCode, scanCode, modifiers); 122 | } 123 | 124 | @Override 125 | protected boolean onKeyRelease(int keyCode, int scanCode, int modifiers) { 126 | return this.getFocused() != null && this.getFocused().keyReleased(keyCode, scanCode, modifiers); 127 | } 128 | 129 | @Override 130 | protected boolean onCharTyped(char chr, int keyCode) { 131 | return this.getFocused() != null && this.getFocused().charTyped(chr, keyCode); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/container/SpruceContainerWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget.container; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.background.Background; 14 | import dev.lambdaurora.spruceui.background.EmptyBackground; 15 | import dev.lambdaurora.spruceui.border.Border; 16 | import dev.lambdaurora.spruceui.border.EmptyBorder; 17 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 18 | import dev.lambdaurora.spruceui.widget.WithBackground; 19 | import dev.lambdaurora.spruceui.widget.WithBorder; 20 | import net.minecraft.client.gui.GuiGraphics; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.function.Consumer; 25 | 26 | /** 27 | * Represents a container widget. 28 | * 29 | * @author LambdAurora 30 | * @version 5.0.0 31 | * @since 2.0.0 32 | */ 33 | public class SpruceContainerWidget extends AbstractSpruceParentWidget implements WithBackground, WithBorder { 34 | private final List children = new ArrayList<>(); 35 | private Background background = EmptyBackground.EMPTY_BACKGROUND; 36 | private Border border = EmptyBorder.EMPTY_BORDER; 37 | 38 | public SpruceContainerWidget(Position position, int width, int height) { 39 | super(position, SpruceWidget.class); 40 | this.width = width; 41 | this.height = height; 42 | } 43 | 44 | @Override 45 | public Background getBackground() { 46 | return this.background; 47 | } 48 | 49 | @Override 50 | public void setBackground(Background background) { 51 | this.background = background; 52 | } 53 | 54 | @Override 55 | public Border getBorder() { 56 | return this.border; 57 | } 58 | 59 | @Override 60 | public void setBorder(Border border) { 61 | this.border = border; 62 | } 63 | 64 | public void addChild(SpruceWidget child) { 65 | this.setOwnerShip(child); 66 | this.children.add(child); 67 | } 68 | 69 | public void addChildren(ChildrenFactory childrenFactory) { 70 | childrenFactory.build(this.width, this.height, this::addChild); 71 | } 72 | 73 | @Override 74 | public List children() { 75 | return this.children; 76 | } 77 | 78 | /* Rendering */ 79 | 80 | @Override 81 | protected void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 82 | this.forEach(child -> child.render(graphics, mouseX, mouseY, delta)); 83 | this.getBorder().render(graphics, this, mouseX, mouseY, delta); 84 | } 85 | 86 | @Override 87 | protected void renderBackground(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 88 | this.getBackground().render(graphics, this, 0, mouseX, mouseY, delta); 89 | } 90 | 91 | public interface ChildrenFactory { 92 | void build(int containerWidth, int containerHeight, Consumer widgetAdder); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/container/SpruceParentWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget.container; 11 | 12 | import dev.lambdaurora.spruceui.navigation.NavigationDirection; 13 | import dev.lambdaurora.spruceui.navigation.NavigationUtils; 14 | import dev.lambdaurora.spruceui.widget.SpruceWidget; 15 | import org.jetbrains.annotations.Nullable; 16 | 17 | import java.util.Iterator; 18 | import java.util.List; 19 | import java.util.Optional; 20 | 21 | /** 22 | * Represents a parent widget. 23 | * 24 | * @author LambdAurora 25 | * @version 3.3.0 26 | * @since 2.0.4 27 | */ 28 | public interface SpruceParentWidget extends SpruceWidget, Iterable { 29 | /** 30 | * Returns the children of this parent widget. 31 | * 32 | * @return the children 33 | */ 34 | List children(); 35 | 36 | @Override 37 | default Iterator iterator() { 38 | return this.children().iterator(); 39 | } 40 | 41 | /** 42 | * Returns the focused element in this widget. 43 | * 44 | * @return the focused element in this widget, may be {@code null} if none is focused 45 | */ 46 | @Nullable E getFocused(); 47 | 48 | /** 49 | * Sets the focused element in this widget. 50 | * 51 | * @param focused the focused element in this widget, may be {@code null} to remove focus. 52 | */ 53 | void setFocused(@Nullable E focused); 54 | 55 | /** 56 | * Returns the potential hovered element at the given mouse coordinates. 57 | * 58 | * @param mouseX the mouse X-coordinate 59 | * @param mouseY the mouse Y-coordinate 60 | * @return the hovered element if it exists, may be empty if none is present at the given coordinates 61 | */ 62 | default Optional hoveredElement(double mouseX, double mouseY) { 63 | var it = this.children().iterator(); 64 | 65 | E element; 66 | do { 67 | if (!it.hasNext()) { 68 | return Optional.empty(); 69 | } 70 | 71 | element = it.next(); 72 | } while (!element.isMouseOver(mouseX, mouseY)); 73 | 74 | return Optional.of(element); 75 | } 76 | 77 | /* Navigation */ 78 | 79 | @Override 80 | default boolean onNavigation(NavigationDirection direction, boolean tab) { 81 | if (this.requiresCursor()) return false; 82 | boolean result = NavigationUtils.tryNavigate(direction, tab, this.children(), this.getFocused(), this::setFocused, 83 | false); 84 | if (result) 85 | this.setFocused(true); 86 | return result; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/option/SpruceOptionSliderWidget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget.option; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.option.SpruceDoubleOption; 14 | import dev.lambdaurora.spruceui.widget.SpruceSliderWidget; 15 | import net.minecraft.network.chat.Text; 16 | 17 | /** 18 | * Represents an option slider widget. 19 | * 20 | * @author LambdAurora 21 | * @version 3.3.0 22 | * @since 1.0.0 23 | */ 24 | public class SpruceOptionSliderWidget extends SpruceSliderWidget { 25 | private final SpruceDoubleOption option; 26 | 27 | public SpruceOptionSliderWidget(Position position, int width, int height, SpruceDoubleOption option) { 28 | super(position, width, height, Text.empty(), option.getRatio(option.get()), slider -> option.set(option.getValue(slider.getValue()))); 29 | this.option = option; 30 | this.updateMessage(); 31 | } 32 | 33 | @Override 34 | protected void updateMessage() { 35 | if (this.option != null) 36 | this.setMessage(this.option.getDisplayString()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/widget/text/SpruceTextFieldWidgetBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2024 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.widget.text; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import net.minecraft.network.chat.Text; 14 | import net.minecraft.util.FormattedCharSequence; 15 | 16 | import java.util.Objects; 17 | import java.util.function.BiFunction; 18 | import java.util.function.Consumer; 19 | import java.util.function.Predicate; 20 | 21 | /** 22 | * Represents a text field widget builder. 23 | * 24 | * @author LambdAurora 25 | * @version 6.1.0 26 | * @since 6.1.0 27 | */ 28 | public class SpruceTextFieldWidgetBuilder { 29 | private final Position position; 30 | private final int width; 31 | private final int height; 32 | private Text title; 33 | private Text placeholder; 34 | private Consumer onChange; 35 | private Predicate textPredicate; 36 | private BiFunction renderTextProvider; 37 | 38 | public SpruceTextFieldWidgetBuilder(Position position, int width, int height) { 39 | this.position = position; 40 | this.width = width; 41 | this.height = height; 42 | } 43 | 44 | public SpruceTextFieldWidgetBuilder title(Text title) { 45 | this.title = title; 46 | return this; 47 | } 48 | 49 | public SpruceTextFieldWidgetBuilder placeholder() { 50 | this.placeholder = this.title; 51 | return this; 52 | } 53 | 54 | public SpruceTextFieldWidgetBuilder placeholder(Text placeholder) { 55 | this.placeholder = placeholder; 56 | return this; 57 | } 58 | 59 | public SpruceTextFieldWidgetBuilder onChange(Consumer onChange) { 60 | this.onChange = onChange; 61 | return this; 62 | } 63 | 64 | public SpruceTextFieldWidgetBuilder textPredicate(Predicate textPredicate) { 65 | this.textPredicate = textPredicate; 66 | return this; 67 | } 68 | 69 | public SpruceTextFieldWidgetBuilder renderTextProvider(BiFunction renderTextProvider) { 70 | this.renderTextProvider = renderTextProvider; 71 | return this; 72 | } 73 | 74 | public SpruceTextFieldWidget build() { 75 | Objects.requireNonNull(this.title, "Text fields require a title."); 76 | var widget = new SpruceTextFieldWidget(this.position, this.width, this.height, this.title, this.placeholder); 77 | if (this.onChange != null) widget.setChangedListener(this.onChange); 78 | if (this.textPredicate != null) widget.setTextPredicate(this.textPredicate); 79 | if (this.renderTextProvider != null) widget.setRenderTextProvider(this.renderTextProvider); 80 | return widget; 81 | } 82 | 83 | public SpruceNamedTextFieldWidget buildNamed() { 84 | return new SpruceNamedTextFieldWidget(this.build()); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/dev/lambdaurora/spruceui/wrapper/VanillaButtonWrapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.wrapper; 11 | 12 | import dev.lambdaurora.spruceui.navigation.NavigationDirection; 13 | import dev.lambdaurora.spruceui.widget.AbstractSpruceButtonWidget; 14 | import dev.lambdaurora.spruceui.widget.SpruceElement; 15 | import net.fabricmc.api.EnvType; 16 | import net.fabricmc.api.Environment; 17 | import net.minecraft.client.gui.GuiGraphics; 18 | import net.minecraft.client.gui.components.AbstractWidget; 19 | import net.minecraft.client.gui.narration.NarrationElementOutput; 20 | 21 | /** 22 | * Represents a vanilla button wrapper for SpruceUI's own button widgets. 23 | * 24 | * @author LambdAurora 25 | * @version 5.0.0 26 | * @since 2.0.0 27 | */ 28 | @Environment(EnvType.CLIENT) 29 | public class VanillaButtonWrapper extends AbstractWidget implements SpruceElement { 30 | private final AbstractSpruceButtonWidget widget; 31 | 32 | public VanillaButtonWrapper(AbstractSpruceButtonWidget widget) { 33 | super(widget.getX(), widget.getY(), widget.getWidth(), widget.getHeight(), widget.getMessage()); 34 | this.widget = widget; 35 | } 36 | 37 | @Override 38 | public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float delta) { 39 | this.widget.getPosition().setRelativeY(this.getY()); 40 | this.widget.render(graphics, mouseX, mouseY, delta); 41 | } 42 | 43 | @Override 44 | public boolean mouseClicked(double mouseX, double mouseY, int button) { 45 | return this.widget.mouseClicked(mouseX, mouseY, button); 46 | } 47 | 48 | @Override 49 | public boolean mouseReleased(double mouseX, double mouseY, int button) { 50 | return this.widget.mouseReleased(mouseX, mouseY, button); 51 | } 52 | 53 | @Override 54 | public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { 55 | return this.widget.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); 56 | } 57 | 58 | @Override 59 | public boolean onNavigation(NavigationDirection direction, boolean tab) { 60 | return this.widget.onNavigation(direction, tab); 61 | } 62 | 63 | @Override 64 | public boolean keyPressed(int keyCode, int scanCode, int modifiers) { 65 | return this.widget.keyPressed(keyCode, scanCode, modifiers); 66 | } 67 | 68 | @Override 69 | public boolean keyReleased(final int keyCode, final int scanCode, final int modifiers) { 70 | return this.widget.keyReleased(keyCode, scanCode, modifiers); 71 | } 72 | 73 | @Override 74 | public NarrationPriority narrationPriority() { 75 | return this.widget.narrationPriority(); 76 | } 77 | 78 | @Override 79 | public void updateWidgetNarration(NarrationElementOutput builder) { 80 | this.widget.updateNarration(builder); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/icon.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/lang/de_de.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui.reset": "Zurücksetzen", 3 | "spruceui.narrator.separator": "%s Trennung", 4 | "spruceui.options.generic": "%s: %s", 5 | "spruceui.options.generic.fancy": "Schön", 6 | "spruceui.options.generic.fast": "Schnell", 7 | "spruceui.options.generic.fastest": "Sehr schnell", 8 | "spruceui.options.generic.simple": "Einfach", 9 | "spruceui.widget.separator": "Trennung" 10 | } 11 | -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/lang/en_ud.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui.reset": "ʇesǝᴚ", 3 | "spruceui.gui.unbind": "dnᴉbn∩", 4 | "spruceui.narrator.separator": "roʇaɹɐdǝs %1$s", 5 | "spruceui.narration.toggle_switch": "%2$s etɐts htiw ,%1$s :ɥɔʇᴉʍS ǝןgᵷo⟘", 6 | "spruceui.options.generic": "%2$s :%1$s", 7 | "spruceui.options.generic.fancy": "ʎɔnɐℲ", 8 | "spruceui.options.generic.fast": "ʇsɐℲ", 9 | "spruceui.options.generic.fastest": "tsǝʇsɐℲ", 10 | "spruceui.options.generic.simple": "ǝןdɯᴉS", 11 | "spruceui.widget.separator": "roʇaɹɐdǝS" 12 | } -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui.reset": "Reset", 3 | "spruceui.gui.unbind": "Unbind", 4 | "spruceui.narrator.separator": "%s separator", 5 | "spruceui.narration.toggle_switch": "Toggle Switch: %s, with state %s", 6 | "spruceui.options.generic": "%s: %s", 7 | "spruceui.options.generic.fancy": "Fancy", 8 | "spruceui.options.generic.fast": "Fast", 9 | "spruceui.options.generic.fastest": "Fastest", 10 | "spruceui.options.generic.simple": "Simple", 11 | "spruceui.widget.separator": "Separator" 12 | } -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/lang/es_es.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui.reset": "Reiniciar", 3 | "spruceui.options.generic": "%s: %s" 4 | } -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/lang/es_mx.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui.reset": "Resetear", 3 | "spruceui.gui.unbind": "Desenlazar", 4 | "spruceui.narrator.separator": "%s separador", 5 | "spruceui.options.generic": "%s: %s", 6 | "spruceui.options.generic.fancy": "Detalladas", 7 | "spruceui.options.generic.fast": "Rápidas", 8 | "spruceui.options.generic.fastest": "Más rápidas", 9 | "spruceui.options.generic.simple": "Simple", 10 | "spruceui.widget.separator": "Separador" 11 | } -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/lang/et_ee.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui.reset": "Lähtesta", 3 | "spruceui.gui.unbind": "Vabasta", 4 | "spruceui.narrator.separator": "%s eraldaja", 5 | "spruceui.options.generic": "%s: %s", 6 | "spruceui.options.generic.fancy": "Uhke", 7 | "spruceui.options.generic.fast": "Kiire", 8 | "spruceui.options.generic.fastest": "Kiireim", 9 | "spruceui.options.generic.simple": "Lihtne", 10 | "spruceui.widget.separator": "Eraldaja" 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/lang/fr_ca.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui.reset": "Réinitialiser", 3 | "spruceui.gui.unbind": "Délier", 4 | "spruceui.narrator.separator": "Séparateur %s", 5 | "spruceui.narration.toggle_switch": "Interrupteur à bascule: %s, avec état %s", 6 | "spruceui.options.generic": "%s: %s", 7 | "spruceui.options.generic.fancy": "Détaillé", 8 | "spruceui.options.generic.fast": "Rapide", 9 | "spruceui.options.generic.fastest": "Très rapide", 10 | "spruceui.options.generic.simple": "Simple", 11 | "spruceui.widget.separator": "Séparateur" 12 | } -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/lang/fr_fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui.reset": "Réinitialiser", 3 | "spruceui.gui.unbind": "Délier", 4 | "spruceui.narrator.separator": "Séparateur %s", 5 | "spruceui.narration.toggle_switch": "Interrupteur à bascule: %s, avec état %s", 6 | "spruceui.options.generic": "%s: %s", 7 | "spruceui.options.generic.fancy": "Détaillé", 8 | "spruceui.options.generic.fast": "Rapide", 9 | "spruceui.options.generic.fastest": "Très rapide", 10 | "spruceui.options.generic.simple": "Simple", 11 | "spruceui.widget.separator": "Séparateur" 12 | } -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/lang/hi_in.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui.reset": "रीसेट", 3 | "spruceui.gui.unbind": "अनबाइंड", 4 | "spruceui.narrator.separator": "%s विभाजक", 5 | "spruceui.narration.toggle_switch": "टॉगल स्विच: %s, स्थिति %s के साथ", 6 | "spruceui.options.generic": "%s: %s", 7 | "spruceui.options.generic.fancy": "रोचक", 8 | "spruceui.options.generic.fast": "तेज़", 9 | "spruceui.options.generic.fastest": "सबसे तेज़", 10 | "spruceui.options.generic.simple": "सरल", 11 | "spruceui.widget.separator": "विभाजक" 12 | } -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/lang/it_it.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui.reset": "Ripristina", 3 | "spruceui.gui.unbind": "Disassocia", 4 | "spruceui.narrator.separator": "Separatore %s", 5 | "spruceui.options.generic": "%s: %s", 6 | "spruceui.options.generic.fancy": "Sofisticato", 7 | "spruceui.options.generic.fast": "Semplice", 8 | "spruceui.options.generic.fastest": "Semplicissimo", 9 | "spruceui.options.generic.simple": "Semplice", 10 | "spruceui.widget.separator": "Separatore" 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/lang/pl_pl.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui.reset": "Resetuj", 3 | "spruceui.gui.unbind": "Nieprzypisany", 4 | "spruceui.narrator.separator": "separator %s", 5 | "spruceui.options.generic": "%s: %s", 6 | "spruceui.options.generic.fancy": "Dokładny", 7 | "spruceui.options.generic.fast": "Szybky", 8 | "spruceui.options.generic.fastest": "Najszybszy", 9 | "spruceui.options.generic.simple": "Prosty", 10 | "spruceui.widget.separator": "Separator" 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/lang/ru_ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui.reset": "Сброс", 3 | "spruceui.gui.unbind": "Отвязать", 4 | "spruceui.narrator.separator": "Разделитель %s", 5 | "spruceui.narration.toggle_switch": "Переключатель: %s, положение: %s", 6 | "spruceui.options.generic": "%s: %s", 7 | "spruceui.options.generic.fancy": "Детально", 8 | "spruceui.options.generic.fast": "Быстро", 9 | "spruceui.options.generic.fastest": "Быстрейше", 10 | "spruceui.options.generic.simple": "Упрощённо", 11 | "spruceui.widget.separator": "Разделитель" 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/lang/tt_ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "modmenu.descriptionTranslation.spruceui": "Тик GUI китапханәсе.", 3 | "spruceui.reset": "Ташлау", 4 | "spruceui.gui.unbind": "Бәйләгечне аеру", 5 | "spruceui.narrator.separator": "%s аергычы", 6 | "spruceui.narration.toggle_switch": "%s күчергече, %s кыяммәте", 7 | "spruceui.options.generic": "%s: %s", 8 | "spruceui.options.generic.fancy": "Җентекле", 9 | "spruceui.options.generic.fast": "Тиз", 10 | "spruceui.options.generic.fastest": "Иң тиз", 11 | "spruceui.options.generic.simple": "Гади", 12 | "spruceui.widget.separator": "Аергыч" 13 | } 14 | -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/lang/zh_cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui.reset": "重置", 3 | "spruceui.gui.unbind": "未绑定", 4 | "spruceui.narrator.separator": "%s 分割线", 5 | "spruceui.options.generic": "%s: %s", 6 | "spruceui.options.generic.fancy": "华丽", 7 | "spruceui.options.generic.fast": "快速", 8 | "spruceui.options.generic.fastest": "最快", 9 | "spruceui.options.generic.simple": "简约", 10 | "spruceui.widget.separator": "分割线" 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/lang/zh_tw.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui.reset": "重設", 3 | "spruceui.gui.unbind": "未綁定", 4 | "spruceui.narrator.separator": "%s 分割線", 5 | "spruceui.narration.toggle_switch": "開關:%s,狀態:%s", 6 | "spruceui.options.generic": "%s:%s", 7 | "spruceui.options.generic.fancy": "華麗", 8 | "spruceui.options.generic.fast": "快速", 9 | "spruceui.options.generic.fastest": "最快", 10 | "spruceui.options.generic.simple": "簡約", 11 | "spruceui.widget.separator": "分割線" 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/powertaters/liltaterreloaded/lil_beanos.json: -------------------------------------------------------------------------------- 1 | { 2 | "lil_beanos": "lil_beanos.png", 3 | "irritated_beanos": "irritated_beanos.png" 4 | } 5 | -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/bottom_right_border_separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/bottom_right_border_separator.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/inworld_bottom_right_border_separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/inworld_bottom_right_border_separator.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/inworld_right_border_separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/inworld_right_border_separator.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/inworld_top_right_border_separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/inworld_top_right_border_separator.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/legacy_options_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/legacy_options_background.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/right_border_separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/right_border_separator.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/sprites/border/simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/sprites/border/simple.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/sprites/border/simple.png.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "gui": { 3 | "scaling": { 4 | "type": "nine_slice", 5 | "width": 200, 6 | "height": 20, 7 | "border": 1 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/sprites/border/simple_highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/sprites/border/simple_highlighted.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/sprites/border/simple_highlighted.png.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "gui": { 3 | "scaling": { 4 | "type": "nine_slice", 5 | "width": 200, 6 | "height": 20, 7 | "border": 1 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/sprites/widget/checkbox/checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/sprites/widget/checkbox/checked.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/sprites/widget/checkbox/crossed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/sprites/widget/checkbox/crossed.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/sprites/widget/toggle_switch/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/sprites/widget/toggle_switch/background.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/sprites/widget/toggle_switch/background_highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/sprites/widget/toggle_switch/background_highlighted.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/sprites/widget/toggle_switch/off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/sprites/widget/toggle_switch/off.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/sprites/widget/toggle_switch/off_highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/sprites/widget/toggle_switch/off_highlighted.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/sprites/widget/toggle_switch/on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/sprites/widget/toggle_switch/on.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/sprites/widget/toggle_switch/on_highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/sprites/widget/toggle_switch/on_highlighted.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/gui/top_right_border_separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/gui/top_right_border_separator.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/tater/irritated_beanos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/tater/irritated_beanos.png -------------------------------------------------------------------------------- /src/main/resources/assets/spruceui/textures/tater/lil_beanos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/assets/spruceui/textures/tater/lil_beanos.png -------------------------------------------------------------------------------- /src/main/resources/high_contrast/assets/spruceui/textures/gui/bottom_right_border_separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/high_contrast/assets/spruceui/textures/gui/bottom_right_border_separator.png -------------------------------------------------------------------------------- /src/main/resources/high_contrast/assets/spruceui/textures/gui/inworld_bottom_right_border_separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/high_contrast/assets/spruceui/textures/gui/inworld_bottom_right_border_separator.png -------------------------------------------------------------------------------- /src/main/resources/high_contrast/assets/spruceui/textures/gui/inworld_right_border_separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/high_contrast/assets/spruceui/textures/gui/inworld_right_border_separator.png -------------------------------------------------------------------------------- /src/main/resources/high_contrast/assets/spruceui/textures/gui/inworld_top_right_border_separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/high_contrast/assets/spruceui/textures/gui/inworld_top_right_border_separator.png -------------------------------------------------------------------------------- /src/main/resources/high_contrast/assets/spruceui/textures/gui/right_border_separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/high_contrast/assets/spruceui/textures/gui/right_border_separator.png -------------------------------------------------------------------------------- /src/main/resources/high_contrast/assets/spruceui/textures/gui/sprites/widget/toggle_switch/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/high_contrast/assets/spruceui/textures/gui/sprites/widget/toggle_switch/background.png -------------------------------------------------------------------------------- /src/main/resources/high_contrast/assets/spruceui/textures/gui/sprites/widget/toggle_switch/background_highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/high_contrast/assets/spruceui/textures/gui/sprites/widget/toggle_switch/background_highlighted.png -------------------------------------------------------------------------------- /src/main/resources/high_contrast/assets/spruceui/textures/gui/sprites/widget/toggle_switch/off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/high_contrast/assets/spruceui/textures/gui/sprites/widget/toggle_switch/off.png -------------------------------------------------------------------------------- /src/main/resources/high_contrast/assets/spruceui/textures/gui/sprites/widget/toggle_switch/off_highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/high_contrast/assets/spruceui/textures/gui/sprites/widget/toggle_switch/off_highlighted.png -------------------------------------------------------------------------------- /src/main/resources/high_contrast/assets/spruceui/textures/gui/sprites/widget/toggle_switch/on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/high_contrast/assets/spruceui/textures/gui/sprites/widget/toggle_switch/on.png -------------------------------------------------------------------------------- /src/main/resources/high_contrast/assets/spruceui/textures/gui/sprites/widget/toggle_switch/on_highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/high_contrast/assets/spruceui/textures/gui/sprites/widget/toggle_switch/on_highlighted.png -------------------------------------------------------------------------------- /src/main/resources/high_contrast/assets/spruceui/textures/gui/top_right_border_separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/main/resources/high_contrast/assets/spruceui/textures/gui/top_right_border_separator.png -------------------------------------------------------------------------------- /src/main/resources/spruceui.accesswidener: -------------------------------------------------------------------------------- 1 | accessWidener v2 named 2 | 3 | transitive-accessible method net/minecraft/client/gui/components/AbstractWidget renderScrollingString (Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/client/gui/Font;Lnet/minecraft/network/chat/Text;IIIII)V 4 | transitive-accessible method net/minecraft/client/gui/components/AbstractWidget renderScrollingString (Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/client/gui/Font;Lnet/minecraft/network/chat/Text;IIIIII)V 5 | -------------------------------------------------------------------------------- /src/main/resources/spruceui.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "dev.lambdaurora.spruceui.mixin", 4 | "compatibilityLevel": "JAVA_21", 5 | "client": [ 6 | "MinecraftClientMixin" 7 | ], 8 | "injectors": { 9 | "defaultRequire": 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/testmod/java/dev/lambdaurora/spruceui/test/TestEnum.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.test; 11 | 12 | import dev.lambdaurora.spruceui.util.Nameable; 13 | import net.minecraft.network.chat.Text; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | /** 17 | * Represents a dummy enum. 18 | * 19 | * @author LambdAurora 20 | */ 21 | public enum TestEnum implements Nameable { 22 | FIRST, 23 | SECOND, 24 | THIRD, 25 | ANOTHER_VALUE; 26 | 27 | private final Text text; 28 | 29 | TestEnum() { 30 | this.text = Text.literal(this.getName()); 31 | } 32 | 33 | /** 34 | * Returns the next enum value available. 35 | * 36 | * @return The next available enum value. 37 | */ 38 | public @NotNull TestEnum next() { 39 | var v = values(); 40 | if (v.length == this.ordinal() + 1) 41 | return v[0]; 42 | return v[this.ordinal() + 1]; 43 | } 44 | 45 | /** 46 | * Gets the text of this enum value. 47 | * 48 | * @return The text of this enum value. 49 | */ 50 | public @NotNull Text getText() { 51 | return this.text; 52 | } 53 | 54 | @Override 55 | public @NotNull String getName() { 56 | return this.name().toLowerCase(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/testmod/java/dev/lambdaurora/spruceui/test/gui/SpruceMainMenuScreen.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.test.gui; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.SpruceTexts; 14 | import dev.lambdaurora.spruceui.screen.SpruceScreen; 15 | import dev.lambdaurora.spruceui.widget.SpruceButtonWidget; 16 | import net.minecraft.client.gui.GuiGraphics; 17 | import net.minecraft.client.gui.screens.Screen; 18 | import net.minecraft.network.chat.Text; 19 | import org.jetbrains.annotations.Nullable; 20 | 21 | /** 22 | * Represents a screen to navigate to the different SpruceUI test screens. 23 | * 24 | * @author LambdAurora 25 | */ 26 | public class SpruceMainMenuScreen extends SpruceScreen { 27 | private final Screen parent; 28 | 29 | public SpruceMainMenuScreen(@Nullable Screen parent) { 30 | super(Text.literal("SpruceUI Test Main Menu")); 31 | this.parent = parent; 32 | } 33 | 34 | @Override 35 | protected void init() { 36 | super.init(); 37 | 38 | int startY = this.height / 4 + 48; 39 | this.addRenderableWidget(new SpruceButtonWidget(Position.of(this, this.width / 2 - 100, startY), 200, 20, Text.literal("Option Test"), 40 | btn -> this.client.setScreen(new SpruceOptionScreen(this)))); 41 | this.addRenderableWidget(new SpruceButtonWidget(Position.of(this, this.width / 2 - 100, startY += 25), 200, 20, Text.literal("Text Area Test"), 42 | btn -> this.client.setScreen(new SpruceTextAreaScreen(this)))); 43 | this.addRenderableWidget(new SpruceButtonWidget(Position.of(this, this.width / 2 - 100, startY += 25), 200, 20, Text.literal("Tabbed Screen Test"), 44 | btn -> this.client.setScreen(new SpruceTabbedTestScreen(this)))); 45 | 46 | // Add done button. 47 | this.addRenderableWidget(new SpruceButtonWidget(Position.of(this, this.width / 2 - 75, this.height - 29), 150, 20, SpruceTexts.GUI_DONE, 48 | btn -> this.client.setScreen(this.parent))); 49 | } 50 | 51 | @Override 52 | public void renderTitle(GuiGraphics guiGraphics, int mouseX, int mouseY, float delta) { 53 | guiGraphics.drawCenteredShadowedText(this.font, this.title, this.width / 2, 8, 16777215); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/testmod/java/dev/lambdaurora/spruceui/test/gui/SpruceOptionScreen.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.test.gui; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.SpruceTexts; 14 | import dev.lambdaurora.spruceui.background.DirtTexturedBackground; 15 | import dev.lambdaurora.spruceui.option.SpruceOption; 16 | import dev.lambdaurora.spruceui.screen.SpruceScreen; 17 | import dev.lambdaurora.spruceui.test.SpruceUITest; 18 | import dev.lambdaurora.spruceui.widget.SpruceButtonWidget; 19 | import dev.lambdaurora.spruceui.widget.container.SpruceOptionListWidget; 20 | import net.minecraft.client.gui.GuiGraphics; 21 | import net.minecraft.client.gui.screens.Screen; 22 | import net.minecraft.network.chat.Text; 23 | import org.jetbrains.annotations.Nullable; 24 | 25 | /** 26 | * Represents a screen to test the different {@link SpruceOption} classes. 27 | * 28 | * @author LambdAurora 29 | */ 30 | public class SpruceOptionScreen extends SpruceScreen { 31 | private final Screen parent; 32 | 33 | //private ButtonListWidget list; 34 | private SpruceOptionListWidget list; 35 | 36 | public SpruceOptionScreen(@Nullable Screen parent) { 37 | super(Text.literal("SpruceUI Test Option Menu")); 38 | this.parent = parent; 39 | } 40 | 41 | private int getTextHeight() { 42 | return (5 + this.font.lineHeight) * 3 + 5; 43 | } 44 | 45 | @Override 46 | protected void init() { 47 | super.init(); 48 | 49 | // Button list. 50 | //this.list = new ButtonListWidget(this.client, this.width, this.height, 43, this.height - 29 - this.getTextHeight(), 25); 51 | this.list = SpruceUITest.get().buildOptionList(Position.of(0, 22), this.width, this.height - 35 - 22); 52 | SpruceUITest.get().resetConsumer = btn -> { 53 | // Re-initialize the screen to update all the values. 54 | this.init(this.client, this.client.getWindow().getGuiScaledWidth(), this.client.getWindow().getGuiScaledHeight()); 55 | }; 56 | 57 | this.addRenderableWidget(this.list); 58 | 59 | // Add reset button. You can add option buttons outside a button list widget. GameOptions instance is required because of Vanilla limitations. 60 | //this.addButton(this.resetOption.createButton(this.client.options, this.width / 2 - 155, this.height - 29, 150)); 61 | // Add done button. 62 | this.addRenderableWidget(new SpruceButtonWidget(Position.of(this, this.width / 2 - 155 + 160, this.height - 29), 150, 20, SpruceTexts.GUI_DONE, 63 | btn -> this.client.setScreen(this.parent)).asVanilla()); 64 | } 65 | 66 | @Override 67 | public void renderTitle(GuiGraphics guiGraphics, int mouseX, int mouseY, float delta) { 68 | guiGraphics.drawCenteredShadowedText(this.font, this.title, this.width / 2, 8, 16777215); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/testmod/java/dev/lambdaurora/spruceui/test/gui/SpruceTabbedTestScreen.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.test.gui; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.SpruceTexts; 14 | import dev.lambdaurora.spruceui.screen.SpruceScreen; 15 | import dev.lambdaurora.spruceui.test.SpruceUITest; 16 | import dev.lambdaurora.spruceui.widget.SpruceButtonWidget; 17 | import dev.lambdaurora.spruceui.widget.SpruceLabelWidget; 18 | import dev.lambdaurora.spruceui.widget.container.SpruceContainerWidget; 19 | import dev.lambdaurora.spruceui.widget.container.tabbed.SpruceTabbedWidget; 20 | import net.minecraft.TextFormatting; 21 | import net.minecraft.client.gui.screens.Screen; 22 | import net.minecraft.network.chat.Text; 23 | import org.jetbrains.annotations.Nullable; 24 | 25 | public class SpruceTabbedTestScreen extends SpruceScreen { 26 | private final Screen parent; 27 | 28 | private SpruceTabbedWidget tabbedWidget; 29 | 30 | protected SpruceTabbedTestScreen(@Nullable Screen parent) { 31 | super(Text.literal("Tabbed Screen Test")); 32 | this.parent = parent; 33 | } 34 | 35 | @Override 36 | protected void init() { 37 | super.init(); 38 | this.tabbedWidget = new SpruceTabbedWidget(Position.of(this, 0, 4), this.width, this.height - 35 - 4, this.title); 39 | this.tabbedWidget.addTabEntry(Text.literal("Hello World"), null, (width, height) -> { 40 | var container = new SpruceContainerWidget(Position.origin(), width, height); 41 | container.addChildren((containerWidth, containerHeight, widgetAdder) -> { 42 | widgetAdder.accept(new SpruceLabelWidget(Position.of(0, 16), 43 | Text.literal("Hello World!").withStyle(TextFormatting.WHITE), 44 | containerWidth, true)); 45 | widgetAdder.accept(new SpruceLabelWidget(Position.of(0, 48), 46 | Text.literal("This is a tabbed widget. You can switch tabs by using the list on the left.\n" + 47 | "It also allows quite a good controller support and arrow key navigation.") 48 | .withStyle(TextFormatting.WHITE), 49 | containerWidth, true)); 50 | }); 51 | return container; 52 | }); 53 | this.tabbedWidget.addSeparatorEntry(Text.literal("Separator")); 54 | this.tabbedWidget.addTabEntry(Text.literal("Option Test"), Text.literal("useful for config stuff.").withStyle(TextFormatting.GRAY), 55 | (width, height) -> SpruceUITest.get().buildOptionList(Position.origin(), width, height)); 56 | this.tabbedWidget.addTabEntry(Text.literal("Text Area"), Text.literal("to edit stuff on multiple lines.").withStyle(TextFormatting.GRAY), 57 | (width, height) -> SpruceUITest.buildTextAreaContainer(Position.origin(), width, height, 58 | textArea -> { 59 | }, null)); 60 | this.addRenderableWidget(this.tabbedWidget); 61 | 62 | // Add done button. 63 | this.addRenderableWidget(new SpruceButtonWidget(Position.of(this, this.width / 2 - 75, this.height - 29), 150, 20, SpruceTexts.GUI_DONE, 64 | btn -> this.client.setScreen(this.parent)).asVanilla()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/testmod/java/dev/lambdaurora/spruceui/test/gui/SpruceTextAreaScreen.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.test.gui; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.screen.SpruceScreen; 14 | import dev.lambdaurora.spruceui.test.SpruceUITest; 15 | import dev.lambdaurora.spruceui.widget.text.SpruceTextAreaWidget; 16 | import net.minecraft.client.gui.GuiGraphics; 17 | import net.minecraft.client.gui.screens.Screen; 18 | import net.minecraft.network.chat.Text; 19 | import org.jetbrains.annotations.Nullable; 20 | 21 | /** 22 | * Represents a screen to test the {@link SpruceTextAreaWidget} widget. 23 | * 24 | * @author LambdAurora 25 | */ 26 | public class SpruceTextAreaScreen extends SpruceScreen { 27 | private final Screen parent; 28 | private SpruceTextAreaWidget textArea; 29 | 30 | public SpruceTextAreaScreen(@Nullable Screen parent) { 31 | super(Text.literal("SpruceUI Test TextArea Menu")); 32 | this.parent = parent; 33 | } 34 | 35 | @Override 36 | protected void init() { 37 | super.init(); 38 | 39 | var containerWidget = 40 | SpruceUITest.buildTextAreaContainer(Position.of(this, 0, 50), this.width, this.height - 50, 41 | textArea -> { 42 | if (this.textArea != null) { 43 | textArea.setText(this.textArea.getText()); 44 | } 45 | this.textArea = textArea; 46 | }, btn -> this.client.setScreen(this.parent)); 47 | this.addRenderableWidget(containerWidget); 48 | } 49 | 50 | @Override 51 | public void renderTitle(GuiGraphics guiGraphics, int mouseX, int mouseY, float delta) { 52 | guiGraphics.drawCenteredShadowedText(this.font, this.title, this.width / 2, 8, 16777215); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/testmod/java/dev/lambdaurora/spruceui/test/mixin/TitleScreenMixin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2020 LambdAurora 3 | * 4 | * This file is part of SpruceUI. 5 | * 6 | * Licensed under the MIT license. For more information, 7 | * see the LICENSE file. 8 | */ 9 | 10 | package dev.lambdaurora.spruceui.test.mixin; 11 | 12 | import dev.lambdaurora.spruceui.Position; 13 | import dev.lambdaurora.spruceui.test.gui.SpruceMainMenuScreen; 14 | import dev.lambdaurora.spruceui.widget.SpruceButtonWidget; 15 | import net.minecraft.client.gui.screens.Screen; 16 | import net.minecraft.client.gui.screens.TitleScreen; 17 | import net.minecraft.network.chat.Text; 18 | import org.spongepowered.asm.mixin.Mixin; 19 | import org.spongepowered.asm.mixin.injection.At; 20 | import org.spongepowered.asm.mixin.injection.Inject; 21 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 22 | 23 | @Mixin(TitleScreen.class) 24 | public class TitleScreenMixin extends Screen { 25 | protected TitleScreenMixin(Text title) { 26 | super(title); 27 | } 28 | 29 | @Inject(method = "init", at = @At("RETURN")) 30 | private void onInit(CallbackInfo ci) { 31 | this.addRenderableWidget(new SpruceButtonWidget(Position.of(0, 0), 150, 20, Text.literal("SpruceUI Test Menu"), 32 | btn -> this.client.setScreen(new SpruceMainMenuScreen(this))).asVanilla()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/testmod/resources/assets/spruceui_test/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/src/testmod/resources/assets/spruceui_test/icon.png -------------------------------------------------------------------------------- /src/testmod/resources/assets/spruceui_test/lang/en_ud.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui_test.option.action": "noitpO noiʇɔⱯ ǝןdɯᴉS", 3 | "spruceui_test.option.boolean": "noᴉʇdO nɐǝןooᗺ", 4 | "spruceui_test.option.checkbox": "noᴉʇdO xobʞɔǝɥƆ", 5 | "spruceui_test.option.cycling": "noiʇdO ᵷnᴉןɔʎƆ", 6 | "spruceui_test.option.double": "uoᴉʇdO ǝןbnoᗡ", 7 | "spruceui_test.option.double_input": "noᴉtpO ʇuduI ǝןbnoᗡ", 8 | "spruceui_test.option.float_input": "noᴉtpO tudnI ʇɐoןℲ", 9 | "spruceui_test.option.int_input": "noᴉtpO tudnI ɹeᵷǝʇnI", 10 | "spruceui_test.option.separator": "roʇaɹɐdǝS", 11 | "spruceui_test.option.toggle_switch": "noitdO ɥɔʇᴉʍS ǝןgᵷo⟘" 12 | } -------------------------------------------------------------------------------- /src/testmod/resources/assets/spruceui_test/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui_test.option.action": "Simple Action Option", 3 | "spruceui_test.option.boolean": "Boolean Option", 4 | "spruceui_test.option.checkbox": "Checkbox Option", 5 | "spruceui_test.option.cycling": "Cycling Option", 6 | "spruceui_test.option.double": "Double Option", 7 | "spruceui_test.option.double_input": "Double Input Option", 8 | "spruceui_test.option.float_input": "Float Input Option", 9 | "spruceui_test.option.int_input": "Integer Input Option", 10 | "spruceui_test.option.separator": "Separator", 11 | "spruceui_test.option.toggle_switch": "Toggle Switch Option" 12 | } -------------------------------------------------------------------------------- /src/testmod/resources/assets/spruceui_test/lang/hi_in.json: -------------------------------------------------------------------------------- 1 | { 2 | "spruceui_test.option.action": "सरल क्रिया विकल्प", 3 | "spruceui_test.option.boolean": "बूलियन विकल्प", 4 | "spruceui_test.option.checkbox": "चेकबॉक्स विकल्प", 5 | "spruceui_test.option.cycling": "साइकिल विकल्प", 6 | "spruceui_test.option.double": "दोहरा विकल्प", 7 | "spruceui_test.option.double_input": "दोहरा इनपुट विकल्प", 8 | "spruceui_test.option.float_input": "फ़्लोट इनपुट विकल्प", 9 | "spruceui_test.option.int_input": "पूर्णांक इनपुट विकल्प", 10 | "spruceui_test.option.separator": "सेपरेटर", 11 | "spruceui_test.option.toggle_switch": "स्विच विकल्प टॉगल करें" 12 | } -------------------------------------------------------------------------------- /src/testmod/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "spruceui_test", 4 | "name": "SpruceUI - Test mod", 5 | "version": "${version}", 6 | "description": "Test mod for SpruceUI.", 7 | "authors": [ 8 | "LambdAurora" 9 | ], 10 | "contact": { 11 | "homepage": "https://github.com/LambdAurora/SpruceUI", 12 | "sources": "https://github.com/LambdAurora/SpruceUI.git", 13 | "issues": "https://github.com/LambdAurora/SpruceUI/issues" 14 | }, 15 | "license": "MIT", 16 | "icon": "assets/spruceui_test/icon.png", 17 | "environment": "client", 18 | "entrypoints": { 19 | "client": [ 20 | "dev.lambdaurora.spruceui.test.SpruceUITest" 21 | ] 22 | }, 23 | "mixins": [ 24 | "spruceui.test.mixins.json" 25 | ], 26 | "depends": { 27 | "fabricloader": "*", 28 | "minecraft": "*", 29 | "spruceui": "*" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/testmod/resources/spruceui.test.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "dev.lambdaurora.spruceui.test.mixin", 4 | "compatibilityLevel": "JAVA_8", 5 | "client": [ 6 | "TitleScreenMixin" 7 | ], 8 | "injectors": { 9 | "defaultRequire": 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /textures/gui/checkbox.kra: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LambdAurora/SpruceUI/c0cf0a4acd2e3985f642573c32051d50248843a7/textures/gui/checkbox.kra --------------------------------------------------------------------------------