├── .github └── dependabot.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle.kts ├── buildSrc ├── build.gradle.kts ├── settings.gradle.kts └── src │ └── main │ └── kotlin │ └── me.huanmeng.gui.publish-conventions.gradle.kts ├── core ├── build.gradle.kts └── src │ └── main │ └── java │ └── me │ └── huanmeng │ └── opensource │ ├── bukkit │ ├── Metrics.java │ ├── component │ │ ├── ComponentConvert.java │ │ └── impl │ │ │ └── DefaultComponentConvertImpl.java │ ├── gui │ │ ├── AbstractGui.java │ │ ├── GuiButton.java │ │ ├── GuiManager.java │ │ ├── HGui.java │ │ ├── PackageGuiContext.java │ │ ├── SlotUtil.java │ │ ├── button │ │ │ ├── Button.java │ │ │ ├── ClickButton.java │ │ │ ├── ClickData.java │ │ │ ├── EmptyButton.java │ │ │ ├── SimpleItemButton.java │ │ │ └── function │ │ │ │ ├── PlayerClickCancelInterface.java │ │ │ │ ├── PlayerClickCancelUpdateAllInterface.java │ │ │ │ ├── PlayerClickCancelUpdateInterface.java │ │ │ │ ├── PlayerClickDataInterface.java │ │ │ │ ├── PlayerClickInterface.java │ │ │ │ ├── PlayerItemInterface.java │ │ │ │ ├── PlayerSimpleCancelInterface.java │ │ │ │ ├── PlayerSimpleCancelUpdateAllInterface.java │ │ │ │ ├── PlayerSimpleCancelUpdateInterface.java │ │ │ │ ├── PlayerSimpleClickCancelInterface.java │ │ │ │ ├── PlayerSimpleClickUpdateAllInterface.java │ │ │ │ ├── PlayerSimpleClickUpdateInterface.java │ │ │ │ └── page │ │ │ │ └── PlayerClickPageButtonInterface.java │ │ ├── draw │ │ │ └── GuiDraw.java │ │ ├── enums │ │ │ └── Result.java │ │ ├── event │ │ │ └── InventorySwitchEvent.java │ │ ├── holder │ │ │ └── GuiHolder.java │ │ ├── impl │ │ │ ├── AbstractGuiCustom.java │ │ │ ├── AbstractGuiPage.java │ │ │ ├── CustomGuiPage.java │ │ │ ├── GuiCustom.java │ │ │ ├── GuiPage.java │ │ │ ├── GuiWrappedInventory.java │ │ │ └── page │ │ │ │ ├── PageArea.java │ │ │ │ ├── PageButton.java │ │ │ │ ├── PageButtonType.java │ │ │ │ ├── PageButtonTypes.java │ │ │ │ ├── PageCondition.java │ │ │ │ ├── PageSetting.java │ │ │ │ ├── PageSettings.java │ │ │ │ └── PageSlot.java │ │ ├── interfaces │ │ │ ├── CustomResultHandler.java │ │ │ ├── GuiBottomClick.java │ │ │ ├── GuiClick.java │ │ │ ├── GuiEmptyItemClick.java │ │ │ ├── GuiHandler.java │ │ │ └── GuiTick.java │ │ ├── listener │ │ │ ├── BukkitEventListener.java │ │ │ ├── ListenerAdapter.java │ │ │ └── PaperEventListener.java │ │ └── slot │ │ │ ├── PlayerSlot.java │ │ │ ├── PlayerSlots.java │ │ │ ├── Slot.java │ │ │ ├── Slots.java │ │ │ ├── function │ │ │ ├── ButtonClickInterface.java │ │ │ ├── ButtonPlaceInterface.java │ │ │ └── ButtonSimpleClickInterface.java │ │ │ └── impl │ │ │ ├── slot │ │ │ ├── SlotForward.java │ │ │ ├── SlotImpl.java │ │ │ └── SlotInterface.java │ │ │ └── slots │ │ │ ├── ArraySlots.java │ │ │ ├── ExcludeSlots.java │ │ │ ├── PatternLineSlots.java │ │ │ └── PatternSlots.java │ ├── scheduler │ │ ├── BukkitTaskImpl.java │ │ ├── SchedulerAsync.java │ │ └── SchedulerSync.java │ ├── tick │ │ └── TickManager.java │ └── util │ │ ├── MathUtil.java │ │ ├── Pair.java │ │ └── item │ │ ├── ItemBuilder.java │ │ ├── ItemUtil.java │ │ └── package-info.java │ ├── func │ └── FreezeSupplier.java │ ├── page │ ├── IPaginationExecutor.java │ └── Pagination.java │ └── scheduler │ ├── Scheduler.java │ └── Schedulers.java ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── kotlin-dsl ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── me │ └── huanmeng │ └── opensource │ └── bukkit │ └── gui │ └── dsl │ ├── ButtonDsl.kt │ ├── ButtonItemDsl.kt │ ├── GuiDsl.kt │ ├── ItemDsl.kt │ ├── PageButtonDsl.kt │ ├── PageSettingDsl.kt │ └── SlotDsl.kt └── settings.gradle.kts /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gradle" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | reviewers: 13 | - "huanmeng-qwq" 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | .gradle 6 | !**/build/ 7 | 8 | ### IntelliJ IDEA ### 9 | .idea 10 | *.iws 11 | *.iml 12 | *.ipr 13 | 14 | ### Eclipse ### 15 | .apt_generated 16 | .classpath 17 | .factorypath 18 | .project 19 | .settings 20 | .springBeans 21 | .sts4-cache 22 | 23 | ### NetBeans ### 24 | /nbproject/private/ 25 | /nbbuild/ 26 | /dist/ 27 | /nbdist/ 28 | /.nb-gradle/ 29 | build/ 30 | !**/src/main/**/build/ 31 | !**/src/test/**/build/ 32 | 33 | ### VS Code ### 34 | .vscode/ 35 | 36 | ### Mac OS ### 37 | .DS_Store 38 | 39 | ### Project ### 40 | **/gui/**/Test* 41 | **/resources/plugin.yml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 huanmeng_qwq 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Gui 3 | 4 |
5 | 6 | ![Version](https://img.shields.io/github/v/release/huanmeng-qwq/Gui?style=plastic) 7 | ![Servers](https://img.shields.io/bstats/servers/18670?style=flat-square) 8 | ![Code-Size](https://img.shields.io/github/languages/code-size/huanmeng-qwq/Gui?style=plastic) 9 | ![Repo-Size](https://img.shields.io/github/repo-size/huanmeng-qwq/Gui?style=plastic) 10 | ![License](https://img.shields.io/github/license/huanmeng-qwq/Gui?style=plastic) 11 | ![Language](https://img.shields.io/github/languages/top/huanmeng-qwq/Gui?style=plastic) 12 | ![Last-Commit](https://img.shields.io/github/last-commit/huanmeng-qwq/Gui?style=plastic) 13 |
14 | 15 | Lightweight Inventory API for Bukkit(Paper/Spigot) plugins, with 1.8.8 to 1.21 support. 16 | 17 | ### Useful links 18 | - [Documentation](https://github.com/huanmeng-qwq/Gui/wiki) 19 | - [Bugs & issues report](https://github.com/huanmeng-qwq/Gui/issues) 20 | 21 | ## Features 22 | * Works with all versions from 1.8.8 to 1.21 23 | * Very small (around 3k lines of code with the JavaDoc) and no dependencies 24 | * Easy to use 25 | * Kotlin DSL 26 | * [Adventure](https://github.com/KyoriPowered/adventure) components support 27 | 28 | ### Maven 29 | 30 | ```xml 31 | 32 | 33 | 34 | org.apache.maven.plugins 35 | maven-shade-plugin 36 | 3.2.3 37 | 38 | 39 | package 40 | 41 | shade 42 | 43 | 44 | 45 | 46 | 47 | 48 | me.huanmeng.opensource.bukkit.gui 49 | 50 | com.yourpackage.gui 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | com.huanmeng-qwq 61 | bukkit-gui 62 | 2.5.0-beta2 63 | 64 | 65 | 66 | 67 | 68 | com.huanmeng-qwq 69 | bukkit-gui-kotlin-dsl 70 | 2.5.0-beta2 71 | 72 | 73 | ``` 74 | When using Maven, make sure to build directly with Maven and not with your IDE configuration. (on IntelliJ IDEA: in the `Maven` tab on the right, in `Lifecycle`, use `package`). 75 | 76 | ### Gradle 77 | 78 | ```groovy 79 | plugins { 80 | id("com.gradleup.shadow") version "8.3.0" 81 | } 82 | 83 | repositories { 84 | mavenCentral() 85 | } 86 | 87 | dependencies { 88 | implementation 'com.huanmeng-qwq:bukkit-gui:2.5.0-beta2' 89 | // Kotlin DSL 90 | implementation 'com.huanmeng-qwq:bukkit-gui-kotlin-dsl:2.5.0-beta2' 91 | } 92 | 93 | shadowJar { 94 | // Replace 'com.yourpackage' with the package of your plugin 95 | relocate 'me.huanmeng.opensource', 'com.yourpackage.huanmeng' 96 | } 97 | ``` 98 | 99 | ## Use 100 | 101 | ### Creating a Gui 102 | 103 | Just create a `GuiCustom`: 104 | 105 | #### Java 106 | ```java 107 | import me.huanmeng.opensource.bukkit.gui.impl.GuiCustom; 108 | import me.huanmeng.opensource.bukkit.gui.slot.Slot; 109 | import org.bukkit.Material; 110 | import org.bukkit.entity.Player; 111 | import org.bukkit.inventory.ItemStack; 112 | 113 | public class Example extends JavaPlugin { 114 | @Override 115 | public void onEnable() { 116 | new GuiManager(this); 117 | } 118 | 119 | @Override 120 | public void onDisable() { 121 | GuiManager.instance().close(); 122 | } 123 | 124 | public static void open(Player player) { 125 | GuiCustom gui = new GuiCustom(player); 126 | // Set the line 127 | gui.line(3); 128 | 129 | // Set the title 130 | gui.title("Test Gui"); 131 | 132 | // Add an apple 133 | gui.draw().set(Slot.of(1), Button.of(player-> new ItemStack(Material.APPLE))); 134 | 135 | // Open for player 136 | gui.openGui(); 137 | } 138 | } 139 | ``` 140 | 141 | #### Kotlin DSL 142 |
143 | GuiCustom Dsl 144 | 145 | ```kotlin 146 | import org.bukkit.entity.Player 147 | 148 | fun openGui(player: Player) { 149 | player.openGui { 150 | draw { 151 | setButton(buildSlot(0)) { 152 | var a = 1 153 | showingItem = buildButtonItem { 154 | ItemStack(Material.values()[a++]) 155 | } 156 | updateClick { 157 | it.inventory.addItem(showingItem!!.get(it)) 158 | } 159 | } 160 | } 161 | } 162 | } 163 | ``` 164 |
165 | 166 |
167 | 168 | GuiPage Dsl 169 | 170 | ```kotlin 171 | import org.bukkit.entity.Player 172 | 173 | fun openPageGui(player: Player) { 174 | buildPagedGui { 175 | allItems = buildButtons { 176 | for (i in 0..60) { 177 | button { 178 | showingItem = buildButtonItem(ItemStack(Material.values()[i])) 179 | } 180 | } 181 | } 182 | elementsPerPage = size() - 9 183 | elementSlots = buildSlotsByLine { line -> 184 | return@buildSlotsByLine buildList { 185 | for (i in 0..9 * line) { 186 | add(buildSlot(i)) 187 | } 188 | } 189 | } 190 | pageSetting { 191 | PageSettings.normal(this) 192 | } 193 | }.openGui(player) 194 | } 195 | ``` 196 | 197 |
198 | 199 |
200 | 201 | PageSetting Dsl 202 | 203 | ```kotlin 204 | buildPagedGui { 205 | pageSetting { 206 | buildPageSetting { 207 | button { 208 | buildPageButton { 209 | types(PageButtonTypes.PREVIOUS) 210 | setButton { 211 | showingItem = buildButtonItem(ItemStack(Material.ARROW)) 212 | } 213 | click(PlayerClickPageButtonInterface.simple()) 214 | } 215 | } 216 | button { 217 | buildPageButton { 218 | types(PageButtonTypes.NEXT) 219 | setButton { 220 | showingItem = buildButtonItem(ItemStack(Material.ARROW)) 221 | } 222 | handleClick { _, gui, buttonType -> 223 | buttonType.changePage(gui) 224 | } 225 | } 226 | } 227 | } 228 | } 229 | // Do something... 230 | } 231 | ``` 232 | 233 |
234 | 235 | ## Adventure support 236 | 237 | For servers on modern [PaperMC](https://papermc.io) versions, The Gui project supports 238 | using [Adventure](https://github.com/KyoriPowered/adventure) components instead of strings, 239 | by using the method `gui.title(Component)`. 240 | 241 | # Support 242 | [](https://www.jetbrains.com/?from=https://github.com/huanmeng-qwq/Gui) 243 | 244 | [JetBrains](https://www.jetbrains.com/), creators of the IntelliJ IDEA, 245 | supports Gui with one of their [Open Source Licenses](https://jb.gg/OpenSourceSupport). 246 | IntelliJ IDEA is the recommended IDE for working with Gui. 247 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.api.JavaVersion 2 | import org.gradle.api.tasks.compile.JavaCompile 3 | import org.gradle.api.tasks.javadoc.Javadoc 4 | import org.gradle.kotlin.dsl.`java-library` 5 | import org.gradle.kotlin.dsl.repositories 6 | 7 | plugins { 8 | `java-library` 9 | `kotlin-dsl` 10 | } 11 | 12 | allprojects { 13 | apply(plugin = "java-library") 14 | 15 | repositories { 16 | mavenLocal() 17 | maven { 18 | url = uri("https://oss.sonatype.org/content/groups/public/") 19 | } 20 | 21 | maven { 22 | url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") 23 | } 24 | 25 | maven { 26 | url = uri("https://repo1.maven.org/maven2/") 27 | } 28 | 29 | maven { 30 | url = uri("https://repo.maven.apache.org/maven2/") 31 | } 32 | maven("https://repo.codemc.io/repository/nms/") 33 | } 34 | 35 | group = "com.huanmeng-qwq" 36 | version = "2.5.0-beta2" 37 | 38 | java { 39 | withSourcesJar() 40 | withJavadocJar() 41 | sourceCompatibility = JavaVersion.VERSION_1_8 42 | targetCompatibility = JavaVersion.VERSION_1_8 43 | } 44 | 45 | tasks.withType() { 46 | options.encoding = "UTF-8" 47 | } 48 | 49 | tasks.withType() { 50 | options.encoding = "UTF-8" 51 | if (JavaVersion.current().isJava9Compatible) { 52 | (options as StandardJavadocDocletOptions).addBooleanOption("html5", true) 53 | } 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /buildSrc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `kotlin-dsl` 3 | } 4 | 5 | repositories { 6 | gradlePluginPortal() 7 | } 8 | 9 | dependencies { 10 | implementation(libs.indra) 11 | implementation("cl.franciscosolis.sonatype-central-upload:cl.franciscosolis.sonatype-central-upload.gradle.plugin:1.0.3") 12 | compileOnly(files(libs::class.java.protectionDomain.codeSource.location)) 13 | } -------------------------------------------------------------------------------- /buildSrc/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | dependencyResolutionManagement { 2 | versionCatalogs { 3 | register("libs") { 4 | from(files("../gradle/libs.versions.toml")) // include from parent project 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /buildSrc/src/main/kotlin/me.huanmeng.gui.publish-conventions.gradle.kts: -------------------------------------------------------------------------------- 1 | import groovy.util.Node 2 | import groovy.util.NodeList 3 | 4 | plugins { 5 | `maven-publish` 6 | `java-library` 7 | id("net.kyori.indra") 8 | id("net.kyori.indra.publishing") 9 | id("cl.franciscosolis.sonatype-central-upload") 10 | } 11 | 12 | indra { 13 | github("huanmeng-qwq", "Gui") { 14 | ci(true) 15 | } 16 | mitLicense() 17 | 18 | javaVersions { 19 | target(8) 20 | minimumToolchain(17) 21 | } 22 | 23 | configurePublications { 24 | pom { 25 | url = "https://github.com/huanmeng-qwq/Gui" 26 | description = "Lightweight Inventory API for Bukkit Development" 27 | developers { 28 | developer { 29 | id = "huanmeng-qwq" 30 | name = "huanmeng_qwq" 31 | email = "huanmeng@huanmeng-qwq.com" 32 | } 33 | } 34 | withXml { 35 | project.configurations.compileOnly.get().allDependencies.forEach { dep -> 36 | ((asNode().get("dependencies") as NodeList)[0] as Node).appendNode("dependency").apply { 37 | appendNode("groupId", dep.group) 38 | appendNode("artifactId", dep.name) 39 | appendNode("version", dep.version) 40 | appendNode("scope", "provided") 41 | } 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | tasks.withType().configureEach { 49 | options.compilerArgs = mutableListOf("-Xlint:-deprecation,-unchecked") 50 | } 51 | 52 | fun read(str: String?): String? { 53 | if (str == null) { 54 | return null 55 | } 56 | try { 57 | val file = File(str).let { 58 | if (!it.exists()) { 59 | return@let File(uri(str)) 60 | } 61 | it 62 | } 63 | if (file.exists()) { 64 | return file.readText(Charsets.UTF_8) 65 | } 66 | } catch (_: Exception) { 67 | } 68 | return str 69 | } 70 | 71 | val cleanUpload by tasks.creating(Delete::class) { 72 | setDelete(project.files(project.layout.buildDirectory.dir("sonatype-central-upload"))) 73 | } 74 | 75 | tasks.sonatypeCentralUpload { 76 | // gradle sonatypeCentralUpload -PCENTRAL_USERNAME= -PCENTRAL_PASSWORD= -PCENTRAL_PRIVATE_KEY= -PCENTRAL_PRIVATE_KEY_PWD= -PCENTRAL_PUBLIC_KEY= 77 | val centralUsername = read(System.getenv("MAVEN_USERNAME") ?: findProperty("CENTRAL_USERNAME")?.toString()) 78 | val centralPassword = read(System.getenv("MAVEN_PASSWORD") ?: findProperty("CENTRAL_PASSWORD")?.toString()) 79 | val privateKey = read(System.getenv("MAVEN_PRIVATE_KEY") ?: findProperty("CENTRAL_PRIVATE_KEY")?.toString()) 80 | val privateKeyPwd = 81 | read(System.getenv("MAVEN_PRIVATE_KEY_PWD") ?: findProperty("CENTRAL_PRIVATE_KEY_PWD")?.toString()) 82 | val publicKey = read(System.getenv("MAVEN_PUBLIC_KEY") ?: findProperty("CENTRAL_PUBLIC_KEY")?.toString()) 83 | dependsOn(tasks.build, tasks.generatePomFileForMavenPublication, cleanUpload) 84 | this.username = centralUsername 85 | this.password = centralPassword 86 | this.publishingType = "AUTOMATIC" 87 | this.signingKey = privateKey 88 | this.signingKeyPassphrase = privateKeyPwd 89 | this.publicKey = publicKey 90 | archives = project.layout.buildDirectory.dir("libs").get().asFileTree 91 | pom = file(project.layout.buildDirectory.file("publications/maven/pom-default.xml")) 92 | } 93 | 94 | tasks.test { 95 | onlyIf { !gradle.startParameter.taskNames.contains("sonatypeCentralUpload") } 96 | } 97 | 98 | project.afterEvaluate { 99 | tasks.findByName("shadowJar")?.apply { 100 | onlyIf { !gradle.startParameter.taskNames.contains("sonatypeCentralUpload") } 101 | } 102 | } -------------------------------------------------------------------------------- /core/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("me.huanmeng.gui.publish-conventions") 3 | } 4 | 5 | dependencies { 6 | implementation("net.kyori:adventure-api:4.14.0") 7 | implementation("net.kyori:adventure-platform-bukkit:4.3.1") 8 | implementation("net.kyori:adventure-text-serializer-legacy:4.14.0") 9 | compileOnly("org.spigotmc:spigot-api:1.19.2-R0.1-SNAPSHOT") 10 | } 11 | 12 | tasks.compileJava { 13 | exclude("**/test/**") 14 | } 15 | 16 | tasks.processResources { 17 | exclude("plugin.yml") 18 | } -------------------------------------------------------------------------------- /core/src/main/java/me/huanmeng/opensource/bukkit/component/ComponentConvert.java: -------------------------------------------------------------------------------- 1 | package me.huanmeng.opensource.bukkit.component; 2 | 3 | import me.huanmeng.opensource.bukkit.component.impl.DefaultComponentConvertImpl; 4 | import net.kyori.adventure.text.Component; 5 | import org.bukkit.entity.Player; 6 | import org.checkerframework.checker.nullness.qual.NonNull; 7 | 8 | import java.util.concurrent.atomic.AtomicReference; 9 | 10 | /** 11 | * 2023/5/31
12 | * Gui
13 | * 14 | * @author huanmeng_qwq 15 | */ 16 | public interface ComponentConvert { 17 | AtomicReference INSTANCE = new AtomicReference<>(new DefaultComponentConvertImpl()); 18 | 19 | static ComponentConvert getDefault() { 20 | return INSTANCE.get(); 21 | } 22 | 23 | /** 24 | * 将Component转换为字符串型式的文本 25 | * 26 | * @param player 玩家 27 | * @param text text 28 | * @return 转换后的文本 29 | */ 30 | @NonNull 31 | String convert(@NonNull Player player, @NonNull Component text); 32 | 33 | /** 34 | * 将Component转换为字符串型式的文本 35 | * 36 | * @param text text 37 | * @return 转换后的文本 38 | */ 39 | String convert(@NonNull Component text); 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/me/huanmeng/opensource/bukkit/component/impl/DefaultComponentConvertImpl.java: -------------------------------------------------------------------------------- 1 | package me.huanmeng.opensource.bukkit.component.impl; 2 | 3 | import me.huanmeng.opensource.bukkit.component.ComponentConvert; 4 | import net.kyori.adventure.text.Component; 5 | import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; 6 | import org.bukkit.entity.Player; 7 | import org.checkerframework.checker.nullness.qual.NonNull; 8 | 9 | /** 10 | * 2023/5/31
11 | * Gui
12 | * 13 | * @author huanmeng_qwq 14 | */ 15 | public class DefaultComponentConvertImpl implements ComponentConvert { 16 | @Override 17 | @NonNull 18 | public String convert(@NonNull Player player, @NonNull Component text) { 19 | return convert(text); 20 | } 21 | 22 | @Override 23 | public String convert(@NonNull Component text) { 24 | return LegacyComponentSerializer.legacySection() 25 | .serialize(text); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/me/huanmeng/opensource/bukkit/gui/GuiButton.java: -------------------------------------------------------------------------------- 1 | package me.huanmeng.opensource.bukkit.gui; 2 | 3 | import me.huanmeng.opensource.bukkit.gui.button.Button; 4 | import me.huanmeng.opensource.bukkit.gui.button.ClickData; 5 | import me.huanmeng.opensource.bukkit.gui.enums.Result; 6 | import me.huanmeng.opensource.bukkit.gui.slot.Slot; 7 | import org.bukkit.entity.Player; 8 | import org.checkerframework.checker.nullness.qual.NonNull; 9 | import org.checkerframework.checker.nullness.qual.Nullable; 10 | 11 | import java.util.Objects; 12 | 13 | /** 14 | * 2023/3/17
15 | * Gui
16 | * 17 | * @author huanmeng_qwq 18 | */ 19 | public final class GuiButton { 20 | 21 | 22 | @NonNull 23 | private Slot slot; 24 | 25 | @NonNull 26 | private Button button; 27 | private boolean isPlayerInventory; 28 | 29 | public GuiButton(@NonNull Slot slot, @Nullable Button button) { 30 | this.slot = slot; 31 | this.button = button == null ? Button.empty() : button; 32 | this.isPlayerInventory = this.slot.isPlayer(); 33 | } 34 | 35 | /** 36 | * 点击事件 37 | */ 38 | @NonNull 39 | public Result onClick(@NonNull ClickData clickData) { 40 | return slot.onClick(clickData); 41 | } 42 | 43 | public int getIndex() { 44 | return slot.getIndex(); 45 | } 46 | 47 | public boolean canPlace(Player player) { 48 | return slot.tryPlace(getButton(), player); 49 | } 50 | 51 | @NonNull 52 | public Slot getSlot() { 53 | return slot; 54 | } 55 | 56 | public void setSlot(@NonNull Slot slot) { 57 | this.slot = slot; 58 | } 59 | 60 | @NonNull 61 | public Button getButton() { 62 | return button; 63 | } 64 | 65 | public void setButton(@NonNull Button button) { 66 | this.button = button; 67 | } 68 | 69 | public boolean isPlayerInventory() { 70 | return isPlayerInventory; 71 | } 72 | 73 | @Override 74 | public boolean equals(Object o) { 75 | if (o == null || getClass() != o.getClass()) return false; 76 | GuiButton guiButton = (GuiButton) o; 77 | return isPlayerInventory == guiButton.isPlayerInventory && Objects.equals(slot, guiButton.slot); 78 | } 79 | 80 | @Override 81 | public int hashCode() { 82 | return Objects.hash(slot, isPlayerInventory); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /core/src/main/java/me/huanmeng/opensource/bukkit/gui/HGui.java: -------------------------------------------------------------------------------- 1 | package me.huanmeng.opensource.bukkit.gui; 2 | 3 | import me.huanmeng.opensource.bukkit.gui.impl.GuiPage; 4 | import org.bukkit.entity.Player; 5 | import org.checkerframework.checker.nullness.qual.NonNull; 6 | import org.checkerframework.checker.nullness.qual.Nullable; 7 | 8 | import java.lang.invoke.MethodHandle; 9 | import java.lang.invoke.MethodHandles; 10 | import java.lang.invoke.MethodType; 11 | import java.lang.reflect.Constructor; 12 | import java.util.Arrays; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.UUID; 16 | import java.util.concurrent.ConcurrentHashMap; 17 | import java.util.function.BiFunction; 18 | 19 | /** 20 | * 2023/3/17
21 | * Gui
22 | *

23 | * 简易的Gui包装类 24 | * 25 | * @author huanmeng_qwq 26 | */ 27 | @SuppressWarnings({"unused"}) 28 | public abstract class HGui { 29 | @Nullable 30 | protected HGui from; 31 | @Nullable 32 | protected AbstractGui fromGui; 33 | @NonNull 34 | protected final PackageGuiContext context; 35 | protected boolean allowBack; 36 | 37 | @Nullable 38 | protected MethodHandle constructorHandle; 39 | 40 | protected BiFunction> newInstanceValuesFunction; 41 | 42 | public static final Map BACK_NODE_MAP = new ConcurrentHashMap<>(); 43 | 44 | public HGui(@NonNull Player player) { 45 | this(player, false); 46 | setConstructor(MethodType.methodType(void.class, Player.class), Arrays::asList); 47 | } 48 | 49 | public HGui(@NonNull Player player, boolean allowBack) { 50 | this.context = new PackageGuiContext(player); 51 | this.allowBack = allowBack; 52 | setConstructor(MethodType.methodType(void.class, Player.class, boolean.class), Arrays::asList); 53 | } 54 | 55 | @SuppressWarnings("SameParameterValue") 56 | protected void setConstructor(MethodType methodType, BiFunction> newInstanceValuesFunction) { 57 | this.newInstanceValuesFunction = newInstanceValuesFunction; 58 | try { 59 | MethodHandles.Lookup lookup = MethodHandles.lookup(); 60 | constructorHandle = lookup.findConstructor(getClass(), methodType); 61 | } catch (NoSuchMethodException | IllegalAccessException e) { 62 | constructorHandle = null; 63 | } 64 | } 65 | 66 | public void setNewInstanceValuesFunction(BiFunction> newInstanceValuesFunction) { 67 | this.newInstanceValuesFunction = newInstanceValuesFunction; 68 | } 69 | 70 | protected void setConstructor(Constructor constructor, BiFunction> newInstanceValuesFunction) { 71 | this.newInstanceValuesFunction = newInstanceValuesFunction; 72 | try { 73 | MethodHandles.Lookup lookup = MethodHandles.lookup(); 74 | constructorHandle = lookup.unreflectConstructor(constructor); 75 | } catch (IllegalAccessException e) { 76 | constructorHandle = null; 77 | } 78 | } 79 | 80 | @Nullable 81 | protected abstract AbstractGui gui(); 82 | 83 | public final void open() { 84 | AbstractGui g = gui(); 85 | if (g == null) { 86 | return; 87 | } 88 | try { 89 | if (fromGui != null && fromGui instanceof GuiPage && g instanceof GuiPage) { 90 | ((GuiPage) g).page(Math.min(((GuiPage) fromGui).page(), ((GuiPage) fromGui).pagination().getMaxPage())); 91 | } 92 | } catch (Throwable ignored) { 93 | } 94 | g.setPlayer(context.getPlayer()); 95 | context.gui(g); 96 | if ((allowBack && constructorHandle != null)) { 97 | if (!BACK_NODE_MAP.containsKey(context.getPlayer())) { 98 | BACK_NODE_MAP.put(context.getPlayer(), new Node()); 99 | } else { 100 | Node node = BACK_NODE_MAP.get(context.getPlayer()); 101 | node.next = new Node(); 102 | node.next.prev = node; 103 | BACK_NODE_MAP.put(context.getPlayer(), node.next); 104 | } 105 | if (from == null) { 106 | Node node = BACK_NODE_MAP.get(context.getPlayer()); 107 | node.methodHandle = constructorHandle; 108 | node.newInstanceValuesFunction = newInstanceValuesFunction; 109 | } 110 | } 111 | g.metadata.put("wrapper", this); 112 | g.backRunner(() -> { 113 | Node node = BACK_NODE_MAP.get(context.getPlayer()); 114 | if ((node == null || node.prev == null)) { 115 | g.close(false, true); 116 | BACK_NODE_MAP.remove(g.player); 117 | return; 118 | } 119 | try { 120 | Node prev = node.prev; 121 | MethodHandle methodHandle = prev.methodHandle; 122 | HGui gui; 123 | if (prev.newInstanceValuesFunction != null) { 124 | gui = (HGui) methodHandle.invokeWithArguments(prev.newInstanceValuesFunction.apply(context.getPlayer(), true)); 125 | } else { 126 | gui = (HGui) methodHandle.invoke(context.getPlayer(), true); 127 | } 128 | gui.from = this; 129 | gui.fromGui = g; 130 | gui.open(); 131 | } catch (Throwable e) { 132 | throw new RuntimeException(e); 133 | } 134 | }); 135 | g.whenClose(gui -> g.scheduler().runLater(() -> { 136 | UUID uuid = context.getPlayer().getUniqueId(); 137 | if (!GuiManager.instance().isOpenGui(uuid)) { 138 | BACK_NODE_MAP.remove(context.getPlayer()); 139 | return; 140 | } 141 | AbstractGui nowGui = GuiManager.instance().getUserOpenGui(uuid); 142 | if (nowGui == null) { 143 | return; 144 | } 145 | if (!nowGui.metadata.containsKey("wrapper")) { 146 | BACK_NODE_MAP.remove(context.getPlayer()); 147 | } 148 | }, 1)); 149 | g.openGui(); 150 | whenOpen(); 151 | } 152 | 153 | protected void whenOpen() { 154 | 155 | } 156 | 157 | @Nullable 158 | protected HGui from() { 159 | return from; 160 | } 161 | 162 | @Nullable 163 | protected AbstractGui getFromGui() { 164 | return fromGui; 165 | } 166 | 167 | public static class Node { 168 | private Node prev, next; 169 | private MethodHandle methodHandle; 170 | private BiFunction> newInstanceValuesFunction; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /core/src/main/java/me/huanmeng/opensource/bukkit/gui/PackageGuiContext.java: -------------------------------------------------------------------------------- 1 | package me.huanmeng.opensource.bukkit.gui; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.checkerframework.checker.nullness.qual.NonNull; 5 | import org.checkerframework.checker.nullness.qual.Nullable; 6 | 7 | /** 8 | * 2023/3/17
9 | * Gui
10 | * 11 | * @author huanmeng_qwq 12 | */ 13 | @SuppressWarnings("rawtypes") 14 | public class PackageGuiContext { 15 | @NonNull 16 | private final Player player; 17 | private AbstractGui gui; 18 | 19 | public PackageGuiContext(@NonNull Player player) { 20 | this.player = player; 21 | } 22 | 23 | @NonNull 24 | public Player getPlayer() { 25 | return player; 26 | } 27 | 28 | @Nullable 29 | public AbstractGui getGui() { 30 | return gui; 31 | } 32 | 33 | public void gui(@NonNull AbstractGui gui) { 34 | this.gui = gui; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/me/huanmeng/opensource/bukkit/gui/SlotUtil.java: -------------------------------------------------------------------------------- 1 | package me.huanmeng.opensource.bukkit.gui; 2 | 3 | import org.checkerframework.checker.nullness.qual.NonNull; 4 | 5 | import java.util.Collection; 6 | 7 | /** 8 | * 2023/3/17
9 | * Gui
10 | * 11 | * @author huanmeng_qwq 12 | */ 13 | @SuppressWarnings("unused") 14 | public class SlotUtil { 15 | /** 16 | * 以1开始计算 17 | * 比如3,5 对应箱子的第3行第5格 = 22 18 | * 19 | * @param row 行 20 | * @param column 列 21 | * @return bukkit中的slot(0开始) 22 | */ 23 | public static int getSlot(int row, int column) { 24 | return (row * 9) - (column == 9 ? 1 : 10 - column); 25 | } 26 | 27 | /** 28 | * 通过slot计算行 29 | * 30 | * @param slot slot 31 | * @return 行 32 | */ 33 | public static int getRow(int slot) { 34 | return (slot + 9) / 9; 35 | } 36 | 37 | /** 38 | * 通过slot计算列 39 | * 40 | * @param slot slot 41 | * @return 列 42 | */ 43 | public static int getColumn(int slot) { 44 | return (slot % 9) + 1; 45 | } 46 | 47 | /** 48 | * 通过集合数量建议行数 49 | *

50 | * 对{@link #getLine(int)}的封装 51 | * 52 | * @param items 集合 53 | */ 54 | public static int getLine(@NonNull Collection items) { 55 | return getLine(items.size()); 56 | } 57 | 58 | /** 59 | * 通过数量建议行数 60 | *

61 | * 设计该方法的主要目的是用于 62 | * 有的时候需要将某个list的内容 63 | * 放到gui中, 则可以使用list#size 64 | * 经行计算gui所需的行数 65 | * 66 | * @param size 数量 67 | */ 68 | public static int getLine(int size) { 69 | return (size % 9 == 0 ? size : size + 9) / 9; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /core/src/main/java/me/huanmeng/opensource/bukkit/gui/button/Button.java: -------------------------------------------------------------------------------- 1 | package me.huanmeng.opensource.bukkit.gui.button; 2 | 3 | import me.huanmeng.opensource.bukkit.gui.button.function.PlayerClickCancelInterface; 4 | import me.huanmeng.opensource.bukkit.gui.button.function.PlayerClickInterface; 5 | import me.huanmeng.opensource.bukkit.gui.button.function.PlayerItemInterface; 6 | import me.huanmeng.opensource.bukkit.gui.button.function.PlayerSimpleCancelInterface; 7 | import me.huanmeng.opensource.bukkit.gui.enums.Result; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.inventory.ItemStack; 10 | import org.checkerframework.checker.nullness.qual.NonNull; 11 | import org.checkerframework.checker.nullness.qual.Nullable; 12 | import org.jetbrains.annotations.Contract; 13 | 14 | import java.util.Arrays; 15 | import java.util.Collection; 16 | import java.util.List; 17 | import java.util.function.Function; 18 | import java.util.stream.Collectors; 19 | 20 | /** 21 | * 2023/3/17
22 | * Gui
23 | * 24 | * @author huanmeng_qwq 25 | */ 26 | @SuppressWarnings("unused") 27 | @FunctionalInterface 28 | public interface Button { 29 | /** 30 | * 获取显示物品 31 | * 32 | * @param player 玩家 33 | * @return 物品 34 | */ 35 | @Nullable 36 | ItemStack getShowItem(@NonNull Player player); 37 | 38 | /** 39 | * 空按钮 40 | */ 41 | Button EMPTY = (p) -> null; 42 | 43 | @NonNull 44 | default Result onClick(@NonNull ClickData clickData) { 45 | return Result.CANCEL; 46 | } 47 | 48 | 49 | /** 50 | * 展示型按钮 51 | * 52 | * @param item 物品 53 | * @return {@link Button} 54 | */ 55 | @Contract(value = "!null -> new", pure = true) 56 | static Button of(PlayerItemInterface item) { 57 | return new EmptyButton(item); 58 | } 59 | 60 | /** 61 | * 展示型按钮 62 | * 63 | * @param item 物品 64 | */ 65 | @Contract(value = "_ -> new", pure = true) 66 | static Button of(ItemStack item) { 67 | return new SimpleItemButton(item, PlayerClickInterface.dummy(Result.CANCEL)); 68 | } 69 | 70 | /** 71 | * 点击型按钮 72 | * 73 | * @param item 物品 74 | * @param clickable 点击事件 75 | */ 76 | @Contract(value = "_,_ -> new", pure = true) 77 | static Button of(ItemStack item, PlayerClickInterface clickable) { 78 | return new SimpleItemButton(item, clickable); 79 | } 80 | 81 | /** 82 | * 点击型按钮 83 | * 84 | * @param item 物品 85 | * @param clickable 点击事件 86 | * @return {@link Button} 87 | */ 88 | @Contract(value = "null, null -> new", pure = true) 89 | static Button of(PlayerItemInterface item, PlayerClickInterface clickable) { 90 | return new ClickButton(item, clickable); 91 | } 92 | 93 | /** 94 | * 点击型按钮 95 | * 96 | * @param item 物品 97 | * @param clickable 点击事件 98 | * @return {@link Button} 99 | */ 100 | @Contract(value = "null, !null, null -> new", pure = true) 101 | static <@NonNull T extends PlayerClickInterface> Button of(PlayerItemInterface item, Class cls, T clickable) { 102 | return new ClickButton(item, clickable); 103 | } 104 | 105 | @Contract(value = "null, !null, null -> new", pure = true) 106 | static <@NonNull T extends PlayerClickInterface> Button of(ItemStack item, Class cls, T clickable) { 107 | return new SimpleItemButton(item, clickable); 108 | } 109 | 110 | /** 111 | * 点击型按钮 112 | * 113 | * @param item 物品 114 | * @param clickable 点击事件 115 | * @return {@link Button} 116 | */ 117 | @Contract(value = "null, null -> new", pure = true) 118 | static Button of(PlayerItemInterface item, PlayerClickCancelInterface clickable) { 119 | return new ClickButton(item, clickable); 120 | } 121 | 122 | /** 123 | * 点击型按钮 124 | * 125 | * @param item 物品 126 | * @param playerSimpleCancelInterface 点击事件 127 | * @return {@link Button} 128 | */ 129 | @Contract(value = "_, _ -> new", pure = true) 130 | static Button of(PlayerItemInterface item, PlayerSimpleCancelInterface playerSimpleCancelInterface) { 131 | return new ClickButton(item, playerSimpleCancelInterface); 132 | } 133 | 134 | @Contract(value = "_, _ -> new", pure = true) 135 | static Button of(ItemStack item, PlayerSimpleCancelInterface playerSimpleCancelInterface) { 136 | return new SimpleItemButton(item, playerSimpleCancelInterface); 137 | } 138 | 139 | /** 140 | * 点击型按钮 141 | * 142 | * @param impl 实现了{@link PlayerItemInterface}和{@link PlayerClickInterface}的类 143 | * @param 实现了{@link PlayerItemInterface}和{@link PlayerClickInterface}的类 144 | * @return {@link Button} 145 | */ 146 | @Contract(value = "null -> new", pure = true) 147 | static <@NonNull IMPL extends PlayerItemInterface & PlayerClickInterface> Button ofInstance(IMPL impl) { 148 | return new ClickButton(impl, impl); 149 | } 150 | 151 | /** 152 | * 点击型按钮 153 | * 154 | * @param impl 实现了{@link PlayerItemInterface}和{@link PlayerClickInterface}的类 155 | * @param 实现了{@link PlayerItemInterface}和{@link PlayerClickInterface}的类 156 | * @return {@link Button} 157 | */ 158 | @NonNull 159 | static <@NonNull IMPL extends PlayerItemInterface & PlayerClickInterface> List