├── .gitignore ├── README-chs.md ├── README.md ├── TemplateExample.md ├── pom.xml └── src ├── main ├── java │ └── moe │ │ └── him188 │ │ └── gui │ │ ├── GUIPluginBase.java │ │ ├── element │ │ └── ResponsibleButton.java │ │ ├── kotlin │ │ ├── FormCustomBuilder.kt │ │ ├── FormModalBuilder.kt │ │ ├── FormSimpleBuilder.kt │ │ ├── FormTemplatedBuilder.kt │ │ └── FormWindowKt.kt │ │ ├── template │ │ ├── Template.java │ │ ├── element │ │ │ ├── TemplateElement.java │ │ │ ├── TemplateElementDropdown.java │ │ │ ├── TemplateElementInput.java │ │ │ ├── TemplateElementLabel.java │ │ │ ├── TemplateElementSlider.java │ │ │ ├── TemplateElementStepSlider.java │ │ │ └── TemplateElementToggle.java │ │ └── response │ │ │ ├── TemplateResponse.java │ │ │ └── TemplateResponses.java │ │ ├── utils │ │ ├── Backable.java │ │ ├── ExceptionConsumer.java │ │ ├── ExceptionConsumerAll.java │ │ ├── ExceptionConsumerIgnore.java │ │ ├── ExceptionConsumerNow.java │ │ ├── ExceptionConsumerOnce.java │ │ ├── InputFormatException.java │ │ ├── InputType.java │ │ ├── InputTypeBoolean.java │ │ ├── InputTypeDate.java │ │ ├── InputTypeDouble.java │ │ ├── InputTypeFloat.java │ │ ├── InputTypeInteger.java │ │ ├── InputTypeLevel.java │ │ ├── InputTypeLong.java │ │ ├── InputTypePlayer.java │ │ ├── InputTypeString.java │ │ ├── InputTypeUsername.java │ │ ├── InputTypes.java │ │ ├── KeyAlreadyContainsException.java │ │ ├── NoParentWindowFoundException.java │ │ └── ResponseParseException.java │ │ └── window │ │ ├── FormCustom.java │ │ ├── FormModal.java │ │ ├── FormSimple.java │ │ ├── FormSimpleAdvanced.java │ │ ├── FormSimpleMap.java │ │ ├── FormTemplated.java │ │ ├── ResponsibleFormWindowCustom.java │ │ ├── ResponsibleFormWindowModal.java │ │ ├── ResponsibleFormWindowSimple.java │ │ ├── ResponsibleFormWindowSimpleAdvanced.java │ │ ├── ResponsibleFormWindowSimpleMap.java │ │ ├── ResponsibleFormWindowTemplated.java │ │ ├── WindowManager.java │ │ ├── defaults │ │ └── TipWindow.java │ │ └── listener │ │ ├── action │ │ ├── ClickListener.java │ │ └── ClickListenerSimple.java │ │ └── response │ │ ├── ResponseListener.java │ │ ├── ResponseListenerAdvanced.java │ │ ├── ResponseListenerCustom.java │ │ ├── ResponseListenerModal.java │ │ ├── ResponseListenerSimple.java │ │ └── ResponseListenerTemplate.java └── resources │ └── plugin.yml └── test └── java ├── TestTemplate.java └── TestWindow.java /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff 7 | .idea/ 8 | .idea/**/workspace.xml 9 | .idea/**/missions.xml 10 | .idea/**/dictionaries 11 | .idea/**/shelf 12 | 13 | *.iml 14 | 15 | # Sensitive or high-churn files 16 | .idea/**/dataSources/ 17 | .idea/**/dataSources.ids 18 | .idea/**/dataSources.local.xml 19 | .idea/**/sqlDataSources.xml 20 | .idea/**/dynamic.xml 21 | .idea/**/uiDesigner.xml 22 | .idea/**/dbnavigator.xml 23 | 24 | # Gradle 25 | .idea/**/gradle.xml 26 | .idea/**/libraries 27 | 28 | # CMake 29 | cmake-build-debug/ 30 | cmake-build-release/ 31 | 32 | # Mongo Explorer plugin 33 | .idea/**/mongoSettings.xml 34 | 35 | # File-based project format 36 | *.iws 37 | 38 | # IntelliJ 39 | out/ 40 | 41 | # mpeltonen/sbt-idea plugin 42 | .idea_modules/ 43 | 44 | # JIRA plugin 45 | atlassian-ide-plugin.xml 46 | 47 | # Cursive Clojure plugin 48 | .idea/replstate.xml 49 | 50 | # Crashlytics plugin (for Android Studio and IntelliJ) 51 | com_crashlytics_export_strings.xml 52 | crashlytics.properties 53 | crashlytics-build.properties 54 | fabric.properties 55 | 56 | # Editor-based Rest Client 57 | .idea/httpRequests 58 | ### Maven template 59 | target/ 60 | pom.xml.tag 61 | pom.xml.releaseBackup 62 | pom.xml.versionsBackup 63 | pom.xml.next 64 | release.properties 65 | dependency-reduced-pom.xml 66 | buildNumber.properties 67 | .mvn/timing.properties 68 | 69 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) 70 | !/.mvn/wrapper/maven-wrapper.jar 71 | ### Java template 72 | # Compiled class file 73 | *.class 74 | 75 | # Log file 76 | *.log 77 | 78 | # BlueJ files 79 | *.ctxt 80 | 81 | # Mobile Tools for Java (J2ME) 82 | .mtj.tmp/ 83 | 84 | # Package Files # 85 | #*.jar 86 | *.war 87 | *.nar 88 | *.ear 89 | *.zip 90 | *.tar.gz 91 | *.rar 92 | 93 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 94 | hs_err_pid* 95 | 96 | nukkit/ 97 | 98 | -------------------------------------------------------------------------------- /README-chs.md: -------------------------------------------------------------------------------- 1 | **GUI - FormWindow tools** 2 | 3 | # Introduction 4 | 5 | 这是为开发者制作的插件 6 | 通过使用 **GUI**, 你可以容易地创建表单窗口, 并通过内置的事件方法处理点击和关闭事件! 7 | **欢迎你贡献代码给这个项目!** 无论 PR 还是 issue. 8 | 9 | # Download [![Build Status](https://travis-ci.org/Him188/GUI.svg?branch=master)](https://travis-ci.org/Him188/GUI) 10 | - [TeamCity](http://mamoe.net:2333/viewType.html?buildTypeId=Gui_Build) 11 | 12 | # Features 13 | 14 | ## Kotlin DSL 15 | 16 | Only available in Kotlin 17 | Learn more in [](src/main/java/moe/him188/gui/kotlin/FormWindowKt.kt) 18 | Sample for showing FormSimple 19 | 20 | ```kotlin 21 | player.showFormSimple() { 22 | title("Set title here") 23 | content("Set content here") 24 | 25 | buttons(""Button1", "Button2", "Vararg buttons") 26 | 27 | onClicked {//it: Int 28 | player.sendMessage("You clicked a button whose id is $it") 29 | } 30 | 31 | onClosed {//it: Player 32 | it.sendMessage("You closed the form") 33 | } 34 | } 35 | ``` 36 | 37 | ## ResponseListener 38 | 39 | 每个表单都可以有 **内部**事件检测器([ResponseListener](https://github.com/Him188/GUI/blob/master/src/main/java/moe/him188/gui/window/listener/response/ResponseListener.java)) 40 | 这将会比实现nk Listener, 监听 PlayerFormRespondedEvent 方便许多 41 | 例子 42 | ```java 43 | class A extends ResponsibleFormWindowSimple { 44 | A() { 45 | super("Title", "Content"); 46 | addButton(new ElementButton("Say Hi")); 47 | addButton(new ElementButton("Say Hello")); 48 | } 49 | public void onClicked(int id, Player player) { 50 | switch (id){ 51 | case 0: 52 | player.sendMessage("Hi"); 53 | break; 54 | case 1: 55 | player.sendMessage("Hello"); 56 | break; 57 | } 58 | } 59 | public void onClosed(Player player) { 60 | player.sendMessage("You Closed"); 61 | } 62 | } 63 | ``` 64 | 65 | ## Functional listeners 66 | 67 | **函数式方法(lambda) 支持** 68 | 69 | 这将会大幅缩短创建一些表单的代码, 比如提示窗口 70 | 你可以仅使用一行代码就对玩家发送一个窗口并处理点击按钮事件 71 | 例子: 72 | ` 73 | player.showFormWindow(new ResponsibleFormWindowModal("Tips", "Hello", "Yes", "No") 74 | .onResponded(confirmation -> player.sendMessage("Your choice:"+confirmation))); 75 | ` 76 | [ResponsibleFormWindowSimpleAdvanced.java](src/main/java/moe/him188/gui/window/ResponsibleFormWindowSimpleAdvanced.java): 77 | ```java 78 | public class ResponsibleFormWindowModal extends FormWindowModal implements Backable, ResponseListenerModal { 79 | public final ResponsibleFormWindowModal onClicked(BiConsumer listener) { 80 | this.buttonClickedListener = listener; 81 | return this; 82 | } 83 | public final ResponsibleFormWindowModal onClosed(Consumer listener) { 84 | this.windowClosedListener = listener; 85 | return this; 86 | } 87 | } 88 | ``` 89 | ### Using Listeners 90 | ResponseListener 与 函数式监听器可以同时使用 91 | 注意: **ResponseListener 将会被优先调用** 92 | 93 | ## ResponsibleButton 94 | 提供给 ResponsibleFormWindowSimple 的 **ResponsibleButton** 95 | 不仅仅是窗口, 甚至按钮都可以监听(就像 Android 那样) 96 | 是否觉得上面的写法还是很麻烦? --那么这个实现将会是很好的替代 97 | 例子(可以和上面的相比较): 98 | ```java 99 | class A extends ResponsibleFormWindowSimple { 100 | A() { 101 | super("Title", "Content"); 102 | addButton("Say Hi", player->player.sendMessage("Hi"));//快速添加 103 | addButton(new ResponsibleElementButton("Say Hello", player->player.sendMessage("Hello")));//通常添加 104 | } 105 | public void onClosed(Player player) { 106 | player.sendMessage("You Closed"); 107 | } 108 | } 109 | ``` 110 | 111 | ## ResponsibleFormWindowSimpleAdvanced 112 | 这是为了方便这里贴上代码里面的 javadoc 113 | 我简称 ResponsibleFormWindowSimpleAdvanced 为高级版 114 | 简称 ResponsibleFormWindowSimple 为普通版 115 | ```text 116 | 高级版无需通过 id 自找数据, 一切的一切都由 GUI 帮你完成. 你只需要实现后续处理即可! 117 | 普通版里面, 点击事件函数参数1是 int, 是按钮的 ID. 118 | 并且需要使用 Simple 类型表单的情况, 一般是有一组同一类型的数据需要依次显示. 比如传送世界列表, 任务列表 119 | 在普通版构造器中, 你需要手动对数据遍历并添加按钮, 在普通版click事件中, 你又需要手动从数据(List,Map等)中取出第 n 项, 再处理. 120 | 如果再来一个窗口, 你又要枯燥地重复一切. 那么是时候使用高级版了! 121 | 122 | 高级版里面, 由java泛型支持, 你在构造器中需要传入一组数据和构造按钮的函数, 然后在点击事件中直接拿到按钮对应的数据进行处理 123 | ``` 124 | 请花上一分钟时间读一读 [源代码](src/main/java/moe/him188/gui/window/ResponsibleFormWindowSimpleAdvanced.java), 你就会懂了 125 | 126 | ## ResponsibleFormWindowTemplated 127 | 这是 ResponsibleFormWindow 的实现. 128 | 通过使用它, 你可以通过你自己的 **key** 比如 `enum`, `String` 来获取表单数据. 129 | 模板的 `ElementInput` 拥有内容检查你, 它可以自动检查玩家表单输入是否正确(符合预期格式). 130 | ```text 131 | 比如, 你设置ID为"test"的 Input 只能输入整数, 你在收到返回数据时, 可以通过ID "test" 直接获取玩家的输入内容. 而原本NK表单只有整数ID, 对于项目多达十几个的表单很不方便. 132 | 当玩家提交表单, 模板会将表单数据按照你预期希望的数据类型转换, 因此你可以直接取到整数型, 日期型, 甚至`Level`, `Player`, `Item`类型的数据!(不然, 试想10个输入框, 你需要花几十行判断和转换类型?) 133 | 如果玩家输入数据有误, 你可以选择性地处理错误. 我们提供了 `ExceptionConsumer` 来捕获错误. 也提供了默认的处理方法: 只处理一个错误, 处理每个错误, 集中处理全部错误. 134 | 当你想让玩家重新填写这一项, 你可以连已经填写的其他正确数据, 一起发送表格. 这样能大幅提高表格输入体验.(不然, 试想10个输入框, 填错了一个就全没了?) 135 | ``` 136 | 请查看源码 [`ResponsibleFormWindowTemplated`](https://github.com/Him188/GUI/blob/master/src/main/java/moe/him188/gui/window/ResponsibleFormWindowTemplated.java) 137 | **或者查看模板示例, 快速了解如何使用**: [template example](https://github.com/Him188/GUI/blob/master/TemplateExample.md) 138 | 139 | ## Backable 140 | 所有类型的 `ResponsibleFormWindow` 都支持直接返回上一级窗口. 141 | 并且你只需使用一行代码: 142 | ``` 143 | window.goBack(player); 144 | ``` 145 | 你也可以获取玩家的上一级窗口, 详细请查看源码 [Backable.java](https://github.com/Him188/GUI/blob/master/src/main/java/moe/him188/gui/utils/Backable.java) 146 | 147 | # How to use 148 | ## Maven repository 149 | 150 | 1. Add `repository` in `repositories` 151 | ```xml 152 | 153 | him188-gui 154 | http://mamoe.net:8081/repository/public/ 155 | 156 | ``` 157 | 2. Add `dependency` in `build.dependencies` 158 | ```xml 159 | 160 | moe.him188 161 | gui 162 | LATEST 163 | 164 | ``` 165 | 3. Don't forget to add `depend` into `plugin.yml` 166 | ```yaml 167 | depend: 168 | - GUI 169 | ``` 170 | 171 | ## Package JAR file 172 | 173 | 1. 在项目根目录中运行 `mvn clean package` 174 | 175 | 2. 在 `target/` 中找到构建完成的 jar 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **GUI - FormWindow tools** 2 | **(中文版说明)**[**Chinese README**](https://github.com/Him188/GUI/blob/master/README-chs.md) 3 | 4 | # Introduction 5 | 6 | This plugin is made for developers. 7 | By using **GUI**, you can easily create forms. 8 | **Contribution is welcomed** 9 | 10 | # Download 11 | - [TeamCity](http://mamoe.net:2333/viewType.html?buildTypeId=gui_Build) 12 | 13 | # Features 14 | 15 | ## Kotlin DSL 16 | 17 | Only available in Kotlin 18 | Learn more in [](src/main/java/moe/him188/gui/kotlin/FormWindowKt.kt) 19 | Sample for showing FormSimple 20 | 21 | ```kotlin 22 | player.showFormSimple() { 23 | title("Set title here") 24 | content("Set content here") 25 | 26 | buttons("Button1", "Button2", "Vararg buttons") 27 | 28 | onClicked {//it: Int 29 | player.sendMessage("You clicked a button whose id is $it") 30 | } 31 | 32 | onClosed {//it: Player 33 | it.sendMessage("You closed the form") 34 | } 35 | } 36 | ``` 37 | 38 | ## ResponseListener 39 | 40 | ([ResponseListener.java](src/main/java/moe/him188/gui/window/listener/response/ResponseListener.java)) 41 | Sample: 42 | ```java 43 | class A extends ResponsibleFormWindowSimple { 44 | A() { 45 | super("Title", "Content"); 46 | addButton(new ElementButton("Say Hi")); 47 | addButton(new ElementButton("Say Hello")); 48 | } 49 | public void onClicked(int id, Player player) { 50 | switch (id){ 51 | case 0: 52 | player.sendMessage("Hi"); 53 | break; 54 | case 1: 55 | player.sendMessage("Hello"); 56 | break; 57 | } 58 | } 59 | public void onClosed(Player player) { 60 | player.sendMessage("You Closed"); 61 | } 62 | } 63 | ``` 64 | 65 | ## Functional listeners 66 | 67 | **Functional method(lambda) support** 68 | 69 | You can send FormWindow and process only in one line 70 | Sample: 71 | ` 72 | player.showFormWindow(new FormModal("Tips", "Hello", "Yes", "No") 73 | .onResponded(confirmation -> player.sendMessage("Your choice:"+confirmation))); 74 | ` 75 | 76 | [ResponsibleFormWindowSimpleAdvanced.java](src/main/java/moe/him188/gui/window/ResponsibleFormWindowSimpleAdvanced.java): 77 | ### Using Listeners 78 | 79 | ResponseListener and Functional listeners can be used at the same time 80 | Notice that: **ResponseListener will be called before Functional listeners** 81 | 82 | ## **ResponsibleButton** 83 | 84 | **ResponsibleButton** for ResponsibleFormWindowSimple 85 | Sample: 86 | 87 | ```java 88 | class A extends ResponsibleFormWindowSimple { 89 | A() { 90 | super("Title", "Content"); 91 | addButton("Say Hi", player->player.sendMessage("Hi"));//fast addButton 92 | addButton(new ResponsibleElementButton("Say Hello", player->player.sendMessage("Hello")));//common addButton 93 | } 94 | public void onClosed(Player player) { 95 | player.sendMessage("You Closed"); 96 | } 97 | } 98 | ``` 99 | 100 | ## ResponsibleFormWindowSimpleAdvanced 101 | 102 | ResponsibleFormWindowSimple**Advanced** 103 | This is a wonderful implement of ResponsibleFormWindowSimple! 104 | (I can't translate this paragraph into English) 105 | ```text 106 | 高级版无需通过 id 自找数据, 一切的一切都由 GUI 帮你完成. 你只需要实现后续处理即可! 107 | 普通版里面, 点击事件函数参数1是 int, 是按钮的 ID. 108 | 并且需要使用 Simple 类型表单的情况, 一般是有一组同一类型的数据需要依次显示. 比如传送世界列表, 任务列表 109 | 在普通版构造器中, 你需要手动对数据遍历并添加按钮, 在普通版click事件中, 你又需要手动从数据(List,Map等)中取出第 n 项, 再处理. 110 | 如果再来一个窗口, 你又要枯燥地重复一切. 那么是时候使用高级版了! 111 | 112 | 高级版里面, 由java泛型支持, 你在构造器中需要传入一组数据和构造按钮的函数, 然后在点击事件中直接拿到按钮对应的数据进行处理 113 | ``` 114 | Please use a minute to check the [source code](src/main/java/moe/him188/gui/window/ResponsibleFormWindowSimpleAdvanced.java) 115 | 116 | ## ResponsibleFormWindowTemplated 117 | 118 | extends `ResponsibleFormWindow` 119 | By using it, you can get responses from your custom **key** such as `enum`, `String` 120 | The template has checkers(parsers) to `TemplateElementInput`, It can automatically check whether the input is correct. 121 | (I can't translate this paragraph into English) 122 | ``` 123 | 比如, 你设置ID为"test"的 Input 只能输入整数, 你在收到返回数据时, 可以通过ID "test" 直接获取玩家的输入内容. 而原本NK表单只有整数ID, 对于项目多达十几个的表单很不方便. 124 | 当玩家提交表单, 模板会将表单数据按照你预期希望的数据类型转换, 因此你可以直接取到整数型, 日期型, 甚至`Level`, `Player`, `Item`类型的数据!(不然, 试想10个输入框, 你需要花几十行判断和转换类型?) 125 | 如果玩家输入数据有误, 你可以选择性地处理错误. 我们提供了 `ExceptionConsumer` 来捕获错误. 也提供了默认的处理方法: 只处理一个错误, 处理每个错误, 集中处理全部https://github.com/Him188/GUI/blob/master/src/main/java/moe/him188/gui/window/ResponsibleFormWindowSimpleAdvanced.java错了一个就全没了?) 126 | ``` 127 | Please check the [source code](src/main/java/moe/him188/gui/window/ResponsibleFormWindowTemplated.java) 128 | Or fast know hot wo use: [template example](TemplateExample.md) 129 | 130 | ## Backable 131 | 132 | Each `ResponsibleFormWindow` supports directly back to the window which has been sent before. 133 | And you just need to code one line: 134 | ``` 135 | window.goBack(player); 136 | ``` 137 | You can also get players' latest windows, please view [Backable.java](src/main/java/moe/him188/gui/utils/Backable.java) 138 | 139 | ## How to use 140 | 141 | ### Maven repository 142 | 143 | 1. Add `repository` in `repositories` 144 | ```xml 145 | 146 | mamoe-repo 147 | http://mamoe.net:8081/repository/public/ 148 | 149 | ``` 150 | This repo may permanently work. 151 | 2. Add `dependency` in `build.dependencies` 152 | ```xml 153 | 154 | moe.him188 155 | gui 156 | LATEST 157 | 158 | ``` 159 | Sometimes the 'LATEST' version signal does not work, in this case, you need to use the latest version, such as `1.14` instead. 160 | And, you can get the latest version in `pom.xml` 161 | 3. Don't forget to add `depend` into `plugin.yml` 162 | ```yaml 163 | depend: 164 | - GUI 165 | ``` 166 | 167 | ### Package JAR file 168 | 169 | 1. Run command `mvn clean package` in project root path 170 | 171 | 2. Find JAR in `target/` 172 | -------------------------------------------------------------------------------- /TemplateExample.md: -------------------------------------------------------------------------------- 1 | # Template Example 2 | 3 | First, create a [`Template`](src/main/java/moe/him188/gui/template/Template.java). 4 | [`TestTemplate.java`](src/test/java/TestTemplate.java): 5 | ```java 6 | class TestTemplate extends Template { 7 | public enum ElementKey { 8 | INT_TEST, 9 | DATE_TEST, 10 | STRING_TEST, 11 | DROPDOWN, 12 | TOGGLE, 13 | SLIDER, 14 | STEP_SLIDER, 15 | } 16 | private static TestTemplate instance = new TestTemplate(); 17 | public static TestTemplate getInstance() { 18 | return instance; 19 | } 20 | public TestTemplate() { 21 | setTitle("Test Template"); 22 | addElement(new TemplateElementInput<>(ElementKey.INT_TEST, InputTypes.INTEGER, "int test")); 23 | addElement(new TemplateElementInput<>(ElementKey.DATE_TEST, InputTypes.DATE(new SimpleDateFormat("yyyy/MM/dd HH:mm")), "date test")); 24 | addElement(new TemplateElementInput<>(ElementKey.STRING_TEST, InputTypes.STRING, "string test")); 25 | addElement(new TemplateElementDropdown<>(ElementKey.DROPDOWN, "dropdown", Arrays.asList("1", "2", "3"))); 26 | addElement(new TemplateElementToggle<>(ElementKey.TOGGLE, "toggle", true)); 27 | addElement(new TemplateElementSlider<>(ElementKey.SLIDER, "slider", 0, 100)); 28 | addElement(new TemplateElementStepSlider<>(ElementKey.STEP_SLIDER, "step slider", Arrays.asList("step1", "step2"))); 29 | } 30 | } 31 | ``` 32 | 33 | Second, create a [`ResponsibleFormWindowTemplated`](src/main/java/moe/him188/gui/window/ResponsibleFormWindowTemplated.java) 34 | [`TestWindow.java`](src/test/java/TestWindow.java) 35 | 36 | ```java 37 | class TestWindow extends ResponsibleFormWindowTemplated implements ResponseListenerTemplate { 38 | public TestWindow(Template template) { 39 | super(template); 40 | setExceptionConsumer(new ExceptionConsumerAll() { 41 | @Override 42 | public void accept(Player player, ResponseParseException[] exception) { 43 | StringBuilder message = new StringBuilder(); 44 | for (ResponseParseException parseException : exception) { 45 | if (parseException.getCause() instanceof InputFormatException) { 46 | TemplateElementInput input = (TemplateElementInput) parseException.getElement(); 47 | message.append("Your input ").append(input.getName()).append(" is bad. Pay attention to format: \n ").append(input.getPlaceholder()); 48 | message.append("\n"); 49 | } 50 | } 51 | player.showFormWindow(new TipWindow(message.toString()).onClicked(() -> resendWindow(player))); 52 | } 53 | }); 54 | } 55 | @Override 56 | public void onResponded(TemplateResponses responses, Player player) { 57 | if (responses.hasNull()) { 58 | return; 59 | } 60 | System.out.println(responses.getInputInteger(TestTemplate.ElementKey.INT_TEST)); 61 | System.out.println(responses.getInputDate(TestTemplate.ElementKey.DATE_TEST)); 62 | System.out.println(responses.getInputString(TestTemplate.ElementKey.STRING_TEST)); 63 | System.out.println(responses.getDropdown(TestTemplate.ElementKey.DROPDOWN)); 64 | System.out.println(responses.getToggle(TestTemplate.ElementKey.TOGGLE)); 65 | System.out.println(responses.getSlider(TestTemplate.ElementKey.SLIDER)); 66 | player.showFormWindow(new TipWindow("Success")); 67 | } 68 | } 69 | ``` 70 | 71 | Pay attention to `responses.getInputInteger(TestTemplate.ElementKey.INT_TEST)`, that shows you can get response in your own key instead of int id. 72 | Pay attention to `resendWindow(player)` that shows you can fast resend, and it will keep what player has already typed correctly. 73 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | moe.him188 8 | gui 9 | GUI 10 | 1.15.1 11 | 12 | jar 13 | 14 | 15 | 1.8 16 | 1.8 17 | 1.3.70 18 | UTF-8 19 | 20 | 21 | 22 | package 23 | 24 | 25 | 26 | src/main/resources 27 | 28 | 29 | **/*.* 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.apache.maven.plugins 37 | maven-compiler-plugin 38 | 3.7.0 39 | 40 | 41 | 42 | maven-source-plugin 43 | 2.1 44 | 45 | true 46 | 47 | 48 | 49 | compile 50 | 51 | jar 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.apache.maven.plugins 59 | maven-shade-plugin 60 | 3.1.1 61 | 62 | 63 | 64 | 65 | 66 | package 67 | 68 | shade 69 | 70 | 71 | 72 | 73 | 74 | 75 | org.jetbrains.kotlin 76 | kotlin-maven-plugin 77 | ${kotlin.version} 78 | 79 | 80 | compile 81 | process-sources 82 | 83 | compile 84 | 85 | 86 | 87 | test-compile 88 | test-compile 89 | 90 | test-compile 91 | 92 | 93 | 94 | 95 | 1.8 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | org.jetbrains.kotlin 104 | kotlin-stdlib 105 | ${kotlin.version} 106 | compile 107 | 108 | 109 | 110 | cn.nukkit 111 | nukkit 112 | 1.0-SNAPSHOT 113 | provided 114 | 115 | 116 | 117 | com.intellij 118 | annotations 119 | 12.0 120 | compile 121 | 122 | 123 | 124 | com.google.code.gson 125 | gson 126 | 2.8.5 127 | provided 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/GUIPluginBase.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui; 2 | 3 | import cn.nukkit.Server; 4 | import cn.nukkit.plugin.PluginBase; 5 | import moe.him188.gui.window.WindowManager; 6 | 7 | /** 8 | * @author Him188moe @ GUI Project 9 | */ 10 | public class GUIPluginBase extends PluginBase { 11 | private static GUIPluginBase instance; 12 | private ListenerManager listenerManager; 13 | 14 | public GUIPluginBase() { 15 | super(); 16 | 17 | 18 | instance = this; 19 | listenerManager = new ListenerManager(); 20 | } 21 | 22 | public static GUIPluginBase getInstance() { 23 | return instance; 24 | } 25 | 26 | @Override 27 | public void onEnable() { 28 | super.onEnable(); 29 | listenerManager.register(); 30 | } 31 | 32 | private class ListenerManager { 33 | private WindowManager.RespondedListener respondedListener; 34 | private WindowManager.GoBackListener goBackListener; 35 | 36 | private boolean registered = false; 37 | 38 | private ListenerManager() { 39 | respondedListener = new WindowManager.RespondedListener(); 40 | goBackListener = new WindowManager.GoBackListener(); 41 | } 42 | 43 | private void register() { 44 | if (registered) { 45 | return; 46 | } 47 | registered = true; 48 | Server.getInstance().getPluginManager().registerEvents(goBackListener, GUIPluginBase.this); 49 | Server.getInstance().getPluginManager().registerEvents(respondedListener, GUIPluginBase.this); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/element/ResponsibleButton.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.element; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.form.element.ElementButton; 5 | import cn.nukkit.form.element.ElementButtonImageData; 6 | import cn.nukkit.form.window.FormWindowSimple; 7 | import moe.him188.gui.window.ResponsibleFormWindowSimple; 8 | import moe.him188.gui.window.listener.action.ClickListener; 9 | import moe.him188.gui.window.listener.action.ClickListenerSimple; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.util.Objects; 13 | 14 | /** 15 | * 能监听点击事件的按钮, 注意这个按钮只能用于 {@link ResponsibleFormWindowSimple}, 16 | * 当用于普通 {@link FormWindowSimple} 时任何监听器都不起作用. 17 | * A button for {@link ResponsibleFormWindowSimple}, which can listen the click event. 18 | * When being using on {@link FormWindowSimple}, the listener does not work. 19 | * 20 | * @author Him188moe @ GUI Project 21 | */ 22 | public class ResponsibleButton extends ElementButton { 23 | private transient ClickListener listener = null; 24 | 25 | public ResponsibleButton(@NotNull String text) { 26 | super(Objects.requireNonNull(text)); 27 | } 28 | 29 | public ResponsibleButton(@NotNull String text, @NotNull ClickListener onClicked) { 30 | this(text); 31 | setClickListener(onClicked); 32 | } 33 | 34 | public ResponsibleButton(@NotNull String text, @NotNull ClickListenerSimple onClicked) { 35 | this(text, (ClickListener) onClicked); 36 | } 37 | 38 | public ResponsibleButton(@NotNull String text, @NotNull ElementButtonImageData image) { 39 | super(Objects.requireNonNull(text), image); 40 | } 41 | 42 | public ResponsibleButton(@NotNull String text, @NotNull ElementButtonImageData image, @NotNull ClickListener onClicked) { 43 | this(text, image); 44 | setClickListener(onClicked); 45 | } 46 | 47 | public ResponsibleButton(@NotNull String text, @NotNull ElementButtonImageData image, @NotNull ClickListenerSimple onClicked) { 48 | this(text, image, (ClickListener) onClicked); 49 | } 50 | 51 | public ResponsibleButton setClickListener(@NotNull ClickListener listener) { 52 | Objects.requireNonNull(listener); 53 | this.listener = listener; 54 | return this; 55 | } 56 | 57 | public ResponsibleButton setClickListener(@NotNull ClickListenerSimple listener) { 58 | return this.setClickListener((ClickListener) listener); 59 | } 60 | 61 | public void callClicked(@NotNull Player player) { 62 | if (this.listener != null) { 63 | this.listener.accept(player); 64 | } 65 | } 66 | 67 | public ClickListener getClickListener() { 68 | return listener; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/kotlin/FormCustomBuilder.kt: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.kotlin 2 | 3 | import cn.nukkit.Player 4 | import cn.nukkit.form.element.Element 5 | import cn.nukkit.form.response.FormResponseCustom 6 | import moe.him188.gui.window.FormCustom 7 | import moe.him188.gui.window.ResponsibleFormWindowCustom 8 | 9 | @Suppress("unused") 10 | class FormCustomBuilder { 11 | private var clickListener: (FormResponseCustom) -> Unit = CLICK_LISTENER_NOP 12 | private var closeListener: (Player) -> Unit = CLOSE_LISTENER_NOP 13 | 14 | companion object { 15 | private val CLICK_LISTENER_NOP: (FormResponseCustom) -> Unit = {} 16 | private val CLOSE_LISTENER_NOP: (Player) -> Unit = {} 17 | } 18 | 19 | var title: String = "" 20 | var content: String = "" 21 | 22 | fun title(v: String) { 23 | title = v 24 | } 25 | 26 | fun content(v: String) { 27 | content = v 28 | } 29 | 30 | fun onClicked(listener: (FormResponseCustom) -> Unit) { 31 | clickListener = listener 32 | } 33 | 34 | fun onClosed(listener: (Player) -> Unit) { 35 | closeListener = listener 36 | } 37 | 38 | @PublishedApi 39 | internal val elements: MutableList = mutableListOf() 40 | 41 | fun element(element: Element) { 42 | elements += element 43 | } 44 | 45 | fun element(vararg elements: Element) { 46 | this.elements.addAll(elements) 47 | } 48 | 49 | fun elements(vararg element: Element) { 50 | elements += element 51 | } 52 | 53 | operator fun Element.unaryPlus() { 54 | element(this) 55 | } 56 | 57 | inline fun elements(block: MutableList.() -> Unit) { 58 | elements.apply(block) 59 | } 60 | 61 | fun build(): ResponsibleFormWindowCustom = FormCustom(title, elements).onResponded(clickListener).onClosed(closeListener) 62 | } -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/kotlin/FormModalBuilder.kt: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.kotlin 2 | 3 | import cn.nukkit.Player 4 | import moe.him188.gui.window.FormModal 5 | import moe.him188.gui.window.ResponsibleFormWindowModal 6 | 7 | @Suppress("unused") 8 | class FormModalBuilder { 9 | private lateinit var clickListener: (Boolean) -> Unit 10 | private lateinit var closeListener: (Player) -> Unit 11 | 12 | var title: String = "" 13 | var content: String = "" 14 | var trueButton: String = "" 15 | var falseButton: String = "" 16 | 17 | fun title(v: String) { 18 | title = v 19 | } 20 | 21 | fun content(v: String) { 22 | content = v 23 | } 24 | 25 | fun trueButton(v: String) { 26 | trueButton = v 27 | } 28 | 29 | fun falseButton(v: String) { 30 | falseButton = v 31 | } 32 | 33 | fun onClicked(listener: (Boolean) -> Unit) { 34 | clickListener = listener 35 | } 36 | 37 | fun onClosed(listener: (Player) -> Unit) { 38 | closeListener = listener 39 | } 40 | 41 | fun build(): ResponsibleFormWindowModal = FormModal(title, content, trueButton, falseButton).let { 42 | if (this::clickListener.isInitialized) { 43 | it.onResponded(clickListener) 44 | } 45 | if (this::closeListener.isInitialized) { 46 | it.onClosed(closeListener) 47 | } 48 | return it 49 | } 50 | } -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/kotlin/FormSimpleBuilder.kt: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.kotlin 2 | 3 | import cn.nukkit.Player 4 | import moe.him188.gui.window.FormSimple 5 | import moe.him188.gui.window.ResponsibleFormWindowSimple 6 | 7 | @Suppress("unused") 8 | class FormSimpleBuilder { 9 | var clickListener: (Int) -> Unit = CLICK_LISTENER_NOP 10 | var closeListener: (Player) -> Unit = CLOSE_LISTENER_NOP 11 | 12 | companion object { 13 | private val CLICK_LISTENER_NOP: (Int) -> Unit = {} 14 | private val CLOSE_LISTENER_NOP: (Player) -> Unit = {} 15 | } 16 | 17 | var title: String = "" 18 | var content: String = "" 19 | 20 | fun title(v: String) { 21 | title = v 22 | } 23 | 24 | fun content(v: String) { 25 | content = v 26 | } 27 | 28 | fun onClicked(listener: (Int) -> Unit) { 29 | clickListener = listener 30 | } 31 | 32 | fun onClosed(listener: (Player) -> Unit) { 33 | closeListener = listener 34 | } 35 | 36 | fun button(name: String) { 37 | form.addButton(name) 38 | } 39 | 40 | fun button(vararg name: String) { 41 | name.forEach { 42 | form.addButton(it) 43 | } 44 | } 45 | 46 | fun buttons(vararg name: String) { 47 | name.forEach { 48 | form.addButton(it) 49 | } 50 | } 51 | 52 | operator fun String.unaryPlus() { 53 | button(this) 54 | } 55 | 56 | inline fun buttons(block: ButtonsBuilder.() -> Unit) { 57 | ButtonsBuilder().apply(block) 58 | } 59 | 60 | inner class ButtonsBuilder { 61 | operator fun String.invoke(onClick: (Player) -> Unit) { 62 | form.addButton(this, onClick) 63 | } 64 | } 65 | 66 | @PublishedApi 67 | internal val form = FormSimple(title, content) 68 | 69 | fun build(): ResponsibleFormWindowSimple = form.onClicked(clickListener).onClosed(closeListener) 70 | } -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/kotlin/FormTemplatedBuilder.kt: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.kotlin 2 | 3 | import cn.nukkit.Player 4 | import moe.him188.gui.template.Template 5 | import moe.him188.gui.template.response.TemplateResponses 6 | import moe.him188.gui.window.FormTemplated 7 | import moe.him188.gui.window.ResponsibleFormWindowTemplated 8 | 9 | @Suppress("unused") 10 | class FormTemplatedBuilder { 11 | private var clickListener: (TemplateResponses) -> Unit = CLICK_LISTENER_NOP 12 | private var closeListener: (Player) -> Unit = CLOSE_LISTENER_NOP 13 | 14 | companion object { 15 | private val CLICK_LISTENER_NOP: (TemplateResponses<*>) -> Unit = {} 16 | private val CLOSE_LISTENER_NOP: (Player) -> Unit = {} 17 | } 18 | 19 | fun onClicked(listener: (TemplateResponses) -> Unit) { 20 | clickListener = listener 21 | } 22 | 23 | fun onClosed(listener: (Player) -> Unit) { 24 | closeListener = listener 25 | } 26 | 27 | fun build(template: Template): ResponsibleFormWindowTemplated = FormTemplated(template).onResponded(clickListener).onClosed(closeListener) 28 | } -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/kotlin/FormWindowKt.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package moe.him188.gui.kotlin 4 | 5 | import cn.nukkit.Player 6 | import moe.him188.gui.template.Template 7 | 8 | 9 | inline fun Player.showFormSimple( 10 | listener: FormSimpleBuilder.() -> Unit 11 | ) { 12 | this.showFormWindow(FormSimpleBuilder().apply(listener).build()) 13 | } 14 | 15 | inline fun Player.showFormModal(listener: FormModalBuilder.() -> Unit) { 16 | this.showFormWindow(FormModalBuilder().apply(listener).build()) 17 | } 18 | 19 | inline fun Player.showFormTemplated(template: Template, listener: FormTemplatedBuilder.() -> Unit) { 20 | this.showFormWindow(FormTemplatedBuilder().apply(listener).build(template)) 21 | } 22 | 23 | inline fun Player.showFormCustom(listener: FormCustomBuilder.() -> Unit) { 24 | this.showFormWindow(FormCustomBuilder().apply(listener).build()) 25 | } -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/template/Template.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.template; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.form.response.FormResponseCustom; 5 | import cn.nukkit.form.window.FormWindowCustom; 6 | import moe.him188.gui.template.element.TemplateElement; 7 | import moe.him188.gui.template.response.TemplateResponses; 8 | import moe.him188.gui.utils.ExceptionConsumer; 9 | import moe.him188.gui.utils.ExceptionConsumerIgnore; 10 | import moe.him188.gui.utils.KeyAlreadyContainsException; 11 | import moe.him188.gui.utils.ResponseParseException; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.util.LinkedHashMap; 15 | import java.util.Map; 16 | import java.util.Objects; 17 | 18 | /** 19 | * @param 元素的 key 的类型 20 | * 21 | * @author Him188 @ GUI Project 22 | */ 23 | public class Template { 24 | protected String title = null; 25 | protected String icon = null; 26 | 27 | protected Map> elements = new LinkedHashMap<>(); 28 | 29 | protected Builder builder = new Builder(); 30 | 31 | public void setTitle(String title) { 32 | this.title = title; 33 | } 34 | 35 | public boolean hasTitle() { 36 | return this.title != null; 37 | } 38 | 39 | public String getTitle() { 40 | return this.title; 41 | } 42 | 43 | public String getIcon() { 44 | return this.icon; 45 | } 46 | 47 | public boolean hasIcon() { 48 | return this.icon != null; 49 | } 50 | 51 | public void setIcon(String icon) { 52 | this.icon = icon; 53 | } 54 | 55 | public TemplateElement getElement(K key) { 56 | if (key == null) { 57 | throw new UnsupportedOperationException("can't get elements with null key"); 58 | } 59 | for (TemplateElement value : this.elements.values()) { 60 | if (value.getResponseKey() == key) { 61 | return value; 62 | } 63 | } 64 | 65 | return null; 66 | } 67 | 68 | public Map> getElements() { 69 | return this.elements; 70 | } 71 | 72 | public void addElement(TemplateElement element) throws KeyAlreadyContainsException { 73 | Objects.requireNonNull(element); 74 | checkElementKeyContains(element.getResponseKey()); 75 | this.elements.put(this.elements.size(), element.setId(this.elements.size())); 76 | } 77 | 78 | private void checkElementKeyContains(K key) throws KeyAlreadyContainsException { 79 | if (key == null) { 80 | return; 81 | } 82 | for (TemplateElement value : elements.values()) { 83 | if (value.getResponseKey() == key) { 84 | throw new KeyAlreadyContainsException(key.toString()); 85 | } 86 | } 87 | } 88 | 89 | public Builder getBuilder() { 90 | return builder; 91 | } 92 | 93 | public class Builder { 94 | public void applyTemplateTo(FormWindowCustom window) { 95 | if (hasTitle()) { 96 | window.setTitle(getTitle()); 97 | } 98 | if (hasIcon()) { 99 | window.setIcon(getIcon()); 100 | } 101 | for (TemplateElement element : getElements().values()) { 102 | window.addElement(element.build()); 103 | } 104 | } 105 | 106 | public TemplateResponses parseTemplateResponse(Player player, FormResponseCustom form, @Nullable ExceptionConsumer exceptionConsumer) { 107 | Objects.requireNonNull(player); 108 | Objects.requireNonNull(form); 109 | if (exceptionConsumer == null) { 110 | exceptionConsumer = new ExceptionConsumerIgnore<>(); 111 | } 112 | TemplateResponses responses = new TemplateResponses<>(); 113 | for (Map.Entry entry : form.getResponses().entrySet()) { 114 | if (!getElements().containsKey(entry.getKey())) {//最后一项为null? 115 | break; 116 | } 117 | TemplateElement element = getElements().get(entry.getKey()); 118 | try { 119 | responses.put(element.getResponseKey(), element.parseResponse(entry.getValue())); 120 | } catch (ResponseParseException e) { 121 | responses.put(element.getResponseKey(), null); 122 | exceptionConsumer.catchException(player, e); 123 | } 124 | } 125 | exceptionConsumer.onFinished(player); 126 | return responses; 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/template/element/TemplateElement.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.template.element; 2 | 3 | import cn.nukkit.form.element.Element; 4 | import moe.him188.gui.template.response.TemplateResponse; 5 | import moe.him188.gui.utils.ResponseParseException; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | /** 9 | * 模板的元素 10 | * 11 | * @author Him188moe @ GUI Project 12 | */ 13 | public abstract class TemplateElement { 14 | /** 15 | * 用于模板中通过这个key获取这个element的返回值 16 | */ 17 | private final K responseKey; 18 | 19 | /** 20 | * @Nullable for {@link TemplateElementLabel} 21 | * 22 | * @param responseKey key 23 | */ 24 | public TemplateElement(@Nullable K responseKey) { 25 | this.responseKey = responseKey; 26 | } 27 | 28 | public K getResponseKey() { 29 | return responseKey; 30 | } 31 | 32 | /** 33 | * 用于识别 Response 34 | */ 35 | private int id; 36 | 37 | public int getId() { 38 | return id; 39 | } 40 | 41 | public TemplateElement setId(int id) { 42 | this.id = id; 43 | return this; 44 | } 45 | 46 | public abstract String getName(); 47 | 48 | /** 49 | * Build nukkit element 50 | */ 51 | public abstract Element build(); 52 | 53 | /** 54 | * Parse original response data to Response object. 55 | * 56 | * @param response original response 57 | * 58 | * @return Response object. 59 | */ 60 | public abstract TemplateResponse parseResponse(Object response) throws ResponseParseException; 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/template/element/TemplateElementDropdown.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.template.element; 2 | 3 | import cn.nukkit.form.element.Element; 4 | import cn.nukkit.form.element.ElementDropdown; 5 | import moe.him188.gui.template.response.TemplateResponse; 6 | import moe.him188.gui.utils.ResponseParseException; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * @author Him188moe @ GUI Project 14 | */ 15 | public class TemplateElementDropdown extends TemplateElement { 16 | private final String name; 17 | private final List options; 18 | private final int defaultOption; 19 | 20 | /** 21 | * @see ElementDropdown 22 | */ 23 | public TemplateElementDropdown(@NotNull K elementKey, String name, List options, int defaultOption) { 24 | super(elementKey); 25 | this.name = name; 26 | this.options = options; 27 | this.defaultOption = defaultOption; 28 | } 29 | 30 | public TemplateElementDropdown(@NotNull K elementKey, String name) { 31 | this(elementKey, name, new ArrayList<>()); 32 | } 33 | 34 | public TemplateElementDropdown(@NotNull K elementKey, String name, List options) { 35 | this(elementKey, name, options, 0); 36 | } 37 | 38 | @Override 39 | public String getName() { 40 | return name; 41 | } 42 | 43 | public List getOptions() { 44 | return options; 45 | } 46 | 47 | public String getOption(int id) { 48 | return this.options.get(id); 49 | } 50 | 51 | public int getDefaultOption() { 52 | return defaultOption; 53 | } 54 | 55 | @Override 56 | public Element build() { 57 | return new ElementDropdown(this.name, this.options, this.defaultOption); 58 | } 59 | 60 | @Override 61 | public Response parseResponse(Object object) throws ResponseParseException { 62 | try { 63 | String option = String.valueOf(object); 64 | for (int i = 0; i < this.options.size(); i++) { 65 | if (this.options.get(i).equals(option)) { 66 | return new Response(i, option); 67 | } 68 | } 69 | throw new ResponseParseException(this, new NullPointerException(option)); 70 | } catch (NumberFormatException e) { 71 | throw new ResponseParseException(this, e); 72 | } 73 | } 74 | 75 | public static class Response extends TemplateResponse { 76 | private final String option; 77 | 78 | private Response(int response, String option) { 79 | super(response); 80 | this.option = option; 81 | } 82 | 83 | @Override 84 | public Integer get() { 85 | return response; 86 | } 87 | 88 | public String getOption() { 89 | return option; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/template/element/TemplateElementInput.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.template.element; 2 | 3 | import cn.nukkit.form.element.Element; 4 | import cn.nukkit.form.element.ElementInput; 5 | import moe.him188.gui.template.response.TemplateResponse; 6 | import moe.him188.gui.utils.InputFormatException; 7 | import moe.him188.gui.utils.InputType; 8 | import moe.him188.gui.utils.InputTypes; 9 | import moe.him188.gui.utils.ResponseParseException; 10 | import org.intellij.lang.annotations.MagicConstant; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | /** 14 | * @author Him188moe @ GUI Project 15 | * @see ElementInput 16 | */ 17 | public class TemplateElementInput extends TemplateElement { 18 | private final InputType type; 19 | 20 | private final String name; 21 | private final String placeholder; 22 | private final String defaultText; 23 | 24 | /** 25 | * @param type 可输入的数据类型 26 | * 27 | * @see InputTypes 28 | */ 29 | public TemplateElementInput(@NotNull K elementKey, String name, @MagicConstant(valuesFromClass = InputTypes.class) InputType type, String placeholder, String defaultText) { 30 | super(elementKey); 31 | this.type = type; 32 | 33 | this.name = name; 34 | this.placeholder = placeholder; 35 | this.defaultText = defaultText; 36 | } 37 | 38 | public TemplateElementInput(@NotNull K elementKey, String name, @MagicConstant(valuesFromClass = InputTypes.class) InputType type) { 39 | this(elementKey, name, type, ""); 40 | } 41 | 42 | public TemplateElementInput(@NotNull K elementKey, String name, @MagicConstant(valuesFromClass = InputTypes.class) InputType type, String placeholder) { 43 | this(elementKey, name, type, placeholder, ""); 44 | } 45 | 46 | @Override 47 | public String getName() { 48 | return name; 49 | } 50 | 51 | public String getPlaceholder() { 52 | return placeholder; 53 | } 54 | 55 | public String getDefaultText() { 56 | return defaultText; 57 | } 58 | 59 | public InputType getType() { 60 | return type; 61 | } 62 | 63 | @Override 64 | public Element build() { 65 | return new ElementInput(this.name, this.placeholder, this.defaultText); 66 | } 67 | 68 | @Override 69 | public Response parseResponse(Object object) throws ResponseParseException { 70 | String response = object == null ? "" : object.toString(); 71 | try { 72 | return new Response<>(this.type.parseResponse(response)); 73 | } catch (InputFormatException e) { 74 | throw new ResponseParseException(this, e); 75 | } 76 | } 77 | 78 | public static class Response extends TemplateResponse { 79 | private Response(R response) { 80 | super(response); 81 | } 82 | 83 | @Override 84 | public R get() { 85 | return response; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/template/element/TemplateElementLabel.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.template.element; 2 | 3 | import cn.nukkit.form.element.Element; 4 | import cn.nukkit.form.element.ElementLabel; 5 | import moe.him188.gui.template.response.TemplateResponse; 6 | 7 | /** 8 | * @author Him188moe @ GUI Project 9 | * @see ElementLabel 10 | */ 11 | public class TemplateElementLabel extends TemplateElement { 12 | private final String name; 13 | 14 | public TemplateElementLabel(String name) { 15 | super(null); 16 | 17 | this.name = name; 18 | } 19 | 20 | @Override 21 | public String getName() { 22 | return name; 23 | } 24 | 25 | @Override 26 | public Element build() { 27 | return new ElementLabel(this.name); 28 | } 29 | 30 | @Override 31 | public Response parseResponse(Object object) { 32 | return new Response(); 33 | } 34 | 35 | public static class Response extends TemplateResponse { 36 | private Response() { 37 | super(null); 38 | } 39 | 40 | @Override 41 | public Object get() { 42 | return null; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/template/element/TemplateElementSlider.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.template.element; 2 | 3 | import cn.nukkit.form.element.Element; 4 | import cn.nukkit.form.element.ElementSlider; 5 | import moe.him188.gui.template.response.TemplateResponse; 6 | import moe.him188.gui.utils.ResponseParseException; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * @author Him188moe @ GUI Project 11 | * @see ElementSlider 12 | */ 13 | public class TemplateElementSlider extends TemplateElement { 14 | private final String name; 15 | private final float min;//0 16 | private final float max;//100 17 | private final int step; 18 | private final float defaultValue; 19 | 20 | public TemplateElementSlider(@NotNull K elementKey, String name, int min, int max) { 21 | this(elementKey, name, min, max, -1); 22 | } 23 | 24 | public TemplateElementSlider(@NotNull K elementKey, String name, int min, int max, int step) { 25 | this(elementKey, name, min, max, step, 0f); 26 | } 27 | 28 | public TemplateElementSlider(@NotNull K elementKey, String name, int min, int max, int step, float defaultValue) { 29 | super(elementKey); 30 | this.name = name; 31 | this.min = min; 32 | this.max = max; 33 | if (step != -1f && step > 0) { 34 | this.step = step; 35 | } else { 36 | this.step = 0; 37 | } 38 | this.defaultValue = defaultValue; 39 | } 40 | 41 | @Override 42 | public String getName() { 43 | return name; 44 | } 45 | 46 | public float getMin() { 47 | return min; 48 | } 49 | 50 | public float getMax() { 51 | return max; 52 | } 53 | 54 | public int getStep() { 55 | return step; 56 | } 57 | 58 | public float getDefaultValue() { 59 | return defaultValue; 60 | } 61 | 62 | @Override 63 | public Element build() { 64 | return new ElementSlider(this.name, this.min, this.max, this.step, this.defaultValue); 65 | } 66 | 67 | @Override 68 | public Response parseResponse(Object object) throws ResponseParseException { 69 | try { 70 | return new Response(Float.parseFloat(String.valueOf(object))); 71 | } catch (NumberFormatException e) { 72 | throw new ResponseParseException(this, e); 73 | } 74 | } 75 | 76 | public static class Response extends TemplateResponse { 77 | private Response(float response) { 78 | super(response); 79 | } 80 | 81 | @Override 82 | public Float get() { 83 | return response; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/template/element/TemplateElementStepSlider.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.template.element; 2 | 3 | import cn.nukkit.form.element.Element; 4 | import cn.nukkit.form.element.ElementStepSlider; 5 | import moe.him188.gui.template.response.TemplateResponse; 6 | import moe.him188.gui.utils.ResponseParseException; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.List; 10 | 11 | public class TemplateElementStepSlider extends TemplateElement { 12 | private final String name; 13 | private final List steps; 14 | private final int defaultStepIndex; 15 | 16 | /** 17 | * @param steps size >= 1 18 | */ 19 | public TemplateElementStepSlider(@NotNull K elementKey, String name, List steps) throws IllegalArgumentException { 20 | this(elementKey, name, steps, steps.size() - 1); 21 | } 22 | 23 | /** 24 | * @param steps size >= 1 25 | * @param defaultStep >= 0 26 | */ 27 | public TemplateElementStepSlider(@NotNull K elementKey, String name, List steps, int defaultStep) throws IllegalArgumentException { 28 | super(elementKey); 29 | this.name = name; 30 | this.steps = steps; 31 | if (this.steps.size() == 0) { 32 | throw new IllegalArgumentException("steps must have at least 1 element"); 33 | //or FormWindowCustom#setResponse will throw IndexOutOfBoundsException 34 | } 35 | 36 | this.defaultStepIndex = defaultStep; 37 | } 38 | 39 | @Override 40 | public String getName() { 41 | return name; 42 | } 43 | 44 | public List getSteps() { 45 | return steps; 46 | } 47 | 48 | public String getStep(int id) { 49 | return this.steps.get(id); 50 | } 51 | 52 | public int getDefaultStepIndex() { 53 | return defaultStepIndex; 54 | } 55 | 56 | @Override 57 | public Element build() { 58 | return new ElementStepSlider(this.name, this.steps, this.defaultStepIndex); 59 | } 60 | 61 | @Override 62 | public TemplateElementStepSlider.Response parseResponse(Object object) throws ResponseParseException { 63 | try { 64 | int id = Integer.parseInt(String.valueOf(object)); 65 | return new TemplateElementStepSlider.Response(id, this.getStep(id)); 66 | } catch (NumberFormatException e) { 67 | throw new ResponseParseException(this, e); 68 | } 69 | } 70 | 71 | public static class Response extends TemplateResponse { 72 | private final String step; 73 | 74 | private Response(Integer response, String step) { 75 | super(response); 76 | this.step = step; 77 | } 78 | 79 | @Override 80 | public Integer get() { 81 | return response; 82 | } 83 | 84 | public String getStep() { 85 | return step; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/template/element/TemplateElementToggle.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.template.element; 2 | 3 | import cn.nukkit.form.element.Element; 4 | import cn.nukkit.form.element.ElementToggle; 5 | import moe.him188.gui.template.response.TemplateResponse; 6 | import moe.him188.gui.utils.ResponseParseException; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * @author Him188moe @ GUI Project 11 | * @see ElementToggle 12 | */ 13 | public class TemplateElementToggle extends TemplateElement { 14 | private final String name; 15 | private final boolean defaultValue; 16 | 17 | public TemplateElementToggle(@NotNull K elementKey, String name) { 18 | this(elementKey, name, false); 19 | } 20 | 21 | public TemplateElementToggle(@NotNull K elementKey, String name, boolean defaultValue) { 22 | super(elementKey); 23 | 24 | this.name = name; 25 | this.defaultValue = defaultValue; 26 | } 27 | 28 | @Override 29 | public String getName() { 30 | return name; 31 | } 32 | 33 | public boolean getDefaultValue() { 34 | return defaultValue; 35 | } 36 | 37 | @Override 38 | public Element build() { 39 | return new ElementToggle(this.name, this.defaultValue); 40 | } 41 | 42 | @Override 43 | public Response parseResponse(Object object) throws ResponseParseException { 44 | try { 45 | return new Response(Boolean.parseBoolean(String.valueOf(object))); 46 | } catch (NumberFormatException e) { 47 | throw new ResponseParseException(this, e); 48 | } 49 | } 50 | 51 | public static class Response extends TemplateResponse { 52 | private Response(boolean response) { 53 | super(response); 54 | } 55 | 56 | @Override 57 | public Boolean get() { 58 | return response; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/template/response/TemplateResponse.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.template.response; 2 | 3 | import cn.nukkit.form.response.FormResponse; 4 | 5 | /** 6 | * @author Him188moe @ GUI Project 7 | */ 8 | public abstract class TemplateResponse extends FormResponse { 9 | protected final R response; 10 | 11 | protected TemplateResponse(R response) { 12 | this.response = response; 13 | } 14 | 15 | public R get() { 16 | return response; 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | return String.valueOf(this.response); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/template/response/TemplateResponses.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.template.response; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.form.element.*; 5 | import cn.nukkit.form.window.FormWindowCustom; 6 | import moe.him188.gui.template.element.*; 7 | import moe.him188.gui.utils.*; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.util.*; 11 | 12 | /** 13 | * @param 用于取出 response 的 key 14 | * 15 | * @author Him188moe @ GUI Project 16 | */ 17 | public final class TemplateResponses extends LinkedHashMap> { 18 | private final Builder builder = new Builder(); 19 | 20 | public Builder getBuilder() { 21 | return builder; 22 | } 23 | 24 | /** 25 | * 判断是否存在 null 数据.
26 | * 不存在即代表所有数据均格式正确. 27 | * 28 | * @return true 29 | */ 30 | public boolean hasNull() { 31 | for (TemplateResponse value : this.values()) { 32 | if (value == null) { 33 | return true; 34 | } 35 | } 36 | return false; 37 | } 38 | 39 | /** 40 | * 获取 Input 内容. 注意请勿尝试将 String 类型转 int 类型, 即使输入的是纯数字.
41 | * 不推荐使用这个方法, 因为它不具有安全性, 且容易失误. 请使用 getInputXXX 代替. 42 | * 43 | * @param key key 44 | * @param inputType 用于转换类型. 必须是 {@link InputType} 的泛型或其子类 45 | * @param Input 的数据类型 46 | * 47 | * @return Input 内容 48 | * 49 | * @see InputType 50 | * @see TemplateElementInput 51 | */ 52 | public R getInput(K key, Class inputType) { 53 | return getInputResponse(key, inputType).get(); 54 | } 55 | 56 | /** 57 | * 获取 {@link InputTypeString} 类型 Input 的内容 58 | * 59 | * @param key key 60 | * 61 | * @return Input 内容 62 | * 63 | * @see InputTypeString 64 | * @see TemplateElementInput 65 | */ 66 | public String getInputString(K key) { 67 | return (String) getInputResponse(key).get(); 68 | } 69 | 70 | /** 71 | * 获取 {@link InputTypeDate} 类型 Input 的内容 72 | * 73 | * @param key key 74 | * 75 | * @return Input 内容 76 | * 77 | * @see InputTypeDate 78 | * @see TemplateElementInput 79 | */ 80 | public Date getInputDate(K key) { 81 | return (Date) getInputResponse(key).get(); 82 | } 83 | 84 | /** 85 | * 获取 {@link InputTypeInteger} 类型 Input 的内容 86 | * 87 | * @param key key 88 | * 89 | * @return Input 内容 90 | * 91 | * @see InputTypeInteger 92 | * @see TemplateElementInput 93 | */ 94 | public int getInputInteger(K key) { 95 | return (int) getInputResponse(key).get(); 96 | } 97 | 98 | /** 99 | * 获取 {@link InputTypeLong} 类型 Input 的内容 100 | * 101 | * @param key key 102 | * 103 | * @return Input 内容 104 | * 105 | * @see InputTypeLong 106 | * @see TemplateElementInput 107 | * @since 1.10 108 | */ 109 | public long getInputLong(K key) { 110 | return (long) getInputResponse(key).get(); 111 | } 112 | 113 | /** 114 | * 获取 {@link InputTypePlayer} 类型 Input 的内容 115 | * 116 | * @param key key 117 | * 118 | * @return Input 内容 119 | * 120 | * @see InputTypePlayer 121 | * @see TemplateElementInput 122 | * @since 1.11 123 | */ 124 | public Player getInputPlayer(K key) { 125 | return (Player) getInputResponse(key).get(); 126 | } 127 | 128 | /** 129 | * 获取 {@link InputTypeBoolean} 类型 Input 的内容 130 | * 131 | * @param key key 132 | * 133 | * @return Input 内容 134 | * 135 | * @see InputTypeBoolean 136 | * @see TemplateElementInput 137 | * @since 1.11 138 | */ 139 | public boolean getInputBoolean(K key) { 140 | return (boolean) getInputResponse(key).get(); 141 | } 142 | 143 | /** 144 | * 获取 {@link InputTypeDouble} 类型 Input 的内容 145 | * 146 | * @param key key 147 | * 148 | * @return Input 内容 149 | * 150 | * @see InputTypeDouble 151 | * @see TemplateElementInput 152 | * @since 1.10 153 | */ 154 | public double getInputDouble(K key) { 155 | return (double) getInputResponse(key).get(); 156 | } 157 | 158 | /** 159 | * 获取 {@link InputTypeFloat} 类型 Input 的内容 160 | * 161 | * @param key key 162 | * 163 | * @return Input 内容 164 | * 165 | * @see InputTypeFloat 166 | * @see TemplateElementInput 167 | * @since 1.10 168 | */ 169 | public float getInputFloat(K key) { 170 | return (float) getInputResponse(key).get(); 171 | } 172 | 173 | /** 174 | * 获取选中的项目 id.
175 | * id 从 0 开始. 176 | * 177 | * @param key key 178 | * 179 | * @return 选中的项目 id 180 | * 181 | * @see TemplateElementDropdown 182 | */ 183 | public int getDropdown(K key) { 184 | return getDropdownResponse(key).get(); 185 | } 186 | 187 | /** 188 | * 获取选中的项目内容.
189 | * 190 | * @param key key 191 | * 192 | * @return 选中的项目内容 193 | * 194 | * @see TemplateElementDropdown 195 | */ 196 | public String getDropdownOption(K key) { 197 | return getDropdownResponse(key).getOption(); 198 | } 199 | 200 | /** 201 | * 获取开关的状态 202 | * 203 | * @param key key 204 | * 205 | * @return 开启/关闭 206 | * 207 | * @see TemplateElementToggle 208 | */ 209 | public boolean getToggle(K key) { 210 | return getToggleResponse(key).get(); 211 | } 212 | 213 | /** 214 | * 获取滑块的位置 215 | * 216 | * @param key key 217 | * 218 | * @return 滑块停留的项目 ID 219 | * 220 | * @see TemplateElementStepSlider 221 | */ 222 | public int getStepSlider(K key) { 223 | return getStepSliderResponse(key).get(); 224 | } 225 | 226 | /** 227 | * 获取滑块位置对应的项目 228 | * 229 | * @param key key 230 | * 231 | * @return 滑块停留的项目 232 | * 233 | * @see TemplateElementStepSlider 234 | */ 235 | public String getStepSliderValue(K key) { 236 | return getStepSliderResponse(key).getStep(); 237 | } 238 | 239 | /** 240 | * 获取滑块的位置 241 | * 242 | * @param key key 243 | * 244 | * @return 滑块位置 245 | * 246 | * @see TemplateElementSlider 247 | */ 248 | public float getSlider(K key) { 249 | return getSliderResponse(key).get(); 250 | } 251 | 252 | @SuppressWarnings({"unchecked", "unused"}) 253 | public TemplateElementInput.Response getInputResponse(K key, Class inputType) { 254 | return (TemplateElementInput.Response) get(key); 255 | } 256 | 257 | @SuppressWarnings("unchecked") 258 | private TemplateElementInput.Response getInputResponse(K key) { 259 | return (TemplateElementInput.Response) get(key); 260 | } 261 | 262 | public TemplateElementDropdown.Response getDropdownResponse(K key) { 263 | return (TemplateElementDropdown.Response) get(key); 264 | } 265 | 266 | public TemplateElementToggle.Response getToggleResponse(K key) { 267 | return (TemplateElementToggle.Response) get(key); 268 | } 269 | 270 | public TemplateElementSlider.Response getSliderResponse(K key) { 271 | return (TemplateElementSlider.Response) get(key); 272 | } 273 | 274 | public TemplateElementStepSlider.Response getStepSliderResponse(K key) { 275 | return (TemplateElementStepSlider.Response) get(key); 276 | } 277 | 278 | public final class Builder { 279 | /** 280 | * 将返回数据内容填充到窗口中.
281 | * 填充按照参数 form 元素顺序, 依次填入数据, 请确保类型相同! 282 | * 283 | * @param form form 284 | * @param doNotKeepValues keys that will not be filled. These elements will be defaultValues 285 | */ 286 | public void applyToWindow(FormWindowCustom form, @Nullable K[] doNotKeepValues) { 287 | final List skip = doNotKeepValues == null ? new ArrayList<>() : Arrays.asList(doNotKeepValues); 288 | 289 | Iterator keyIterator = keySet().iterator(); 290 | for (Element element : form.getElements()) { 291 | if (element instanceof ElementLabel) { 292 | continue; 293 | } 294 | 295 | //gets a key with non-null value. 296 | K key = null; 297 | while (key == null || get(key) == null) { 298 | if (!keyIterator.hasNext()) { 299 | throw new RuntimeException("unmatched form"); 300 | } 301 | key = keyIterator.next(); 302 | } 303 | 304 | if (skip.contains(key)) { 305 | continue; 306 | } 307 | 308 | if (element instanceof ElementInput) { 309 | ((ElementInput) element).setDefaultText(getInput(key, Object.class).toString()); 310 | } else if (element instanceof ElementDropdown) { 311 | ((ElementDropdown) element).setDefaultOptionIndex(getDropdown(key)); 312 | } else if (element instanceof ElementSlider) { 313 | ((ElementSlider) element).setDefaultValue(getSlider(key)); 314 | } else if (element instanceof ElementStepSlider) { 315 | ((ElementStepSlider) element).setDefaultOptionIndex(getStepSlider(key)); 316 | } else if (element instanceof ElementToggle) { 317 | ((ElementToggle) element).setDefaultValue(getToggle(key)); 318 | } 319 | } 320 | } 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/Backable.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.form.window.FormWindow; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import java.util.Objects; 8 | 9 | /** 10 | * 标记这个表单支持返回上一级. 11 | * 12 | * @author Him188moe @ GUI Project 13 | */ 14 | public interface Backable { 15 | 16 | void setParent(@Nullable FormWindow window); 17 | 18 | FormWindow getParent(); 19 | 20 | default void goBack(Player player) throws NoParentWindowFoundException { 21 | Objects.requireNonNull(player); 22 | if (getParent() == null) { 23 | throw new NoParentWindowFoundException(); 24 | } 25 | player.showFormWindow(this.getParent()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/ExceptionConsumer.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.form.response.FormResponseCustom; 5 | import moe.him188.gui.template.Template; 6 | 7 | /** 8 | * 异常处理器.
9 | * 用于 {@link Template.Builder#parseTemplateResponse(Player, FormResponseCustom, ExceptionConsumer)} 10 | * 11 | * @author Him188moe @ GUI Project 12 | */ 13 | public interface ExceptionConsumer { 14 | /** 15 | * 接受新的异常
16 | * 但并不代表处理立即这个异常. 17 | * 18 | * @param exception exception 19 | */ 20 | void catchException(Player player, E exception); 21 | 22 | /** 23 | * 处理异常 24 | * 25 | * @param exceptions exception, notnull, maybe empty 26 | */ 27 | void accept(Player player, E[] exceptions); 28 | 29 | /** 30 | * 当需要处理的异常已经全部被 {@link #catchException(Player, Exception)} 时调用 31 | */ 32 | default void onFinished(Player player) { 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/ExceptionConsumerAll.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import cn.nukkit.Player; 4 | 5 | import java.lang.reflect.Array; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * 将所有异常记录并等待 {@link #onFinished(Player)} 后一次性处理 11 | * 12 | * @author Him188moe @ GUI Project 13 | */ 14 | public abstract class ExceptionConsumerAll implements ExceptionConsumer { 15 | private List list = new ArrayList<>(); 16 | private E lastException = null; 17 | 18 | @Override 19 | public void catchException(Player player, E exception) { 20 | list.add(this.lastException = exception); 21 | } 22 | 23 | @SuppressWarnings("unchecked") 24 | @Override 25 | public void onFinished(Player player) { 26 | if (this.lastException != null) { 27 | this.accept(player, list.toArray((E[]) Array.newInstance(lastException.getClass(), 0))); 28 | this.list.clear(); 29 | this.lastException = null; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/ExceptionConsumerIgnore.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import cn.nukkit.Player; 4 | 5 | /** 6 | * 忽略所有异常 7 | * 8 | * @author Him188moe @ GUI Project 9 | */ 10 | public class ExceptionConsumerIgnore implements ExceptionConsumer { 11 | @Override 12 | public void catchException(Player player, E exception) { 13 | 14 | } 15 | 16 | @Override 17 | public void accept(Player player, E[] exceptions) { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/ExceptionConsumerNow.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import cn.nukkit.Player; 4 | 5 | import java.lang.reflect.Array; 6 | 7 | /** 8 | * 收到每个异常立即处理 9 | * 10 | * @author Him188moe @ GUI Project 11 | */ 12 | public abstract class ExceptionConsumerNow implements ExceptionConsumer { 13 | @SuppressWarnings("unchecked") 14 | @Override 15 | public void catchException(Player player, E exception) { 16 | E[] array = (E[]) Array.newInstance(exception.getClass(), 1); 17 | array[0] = exception; 18 | this.accept(player, array); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/ExceptionConsumerOnce.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import cn.nukkit.Player; 4 | 5 | import java.lang.reflect.Array; 6 | 7 | /** 8 | * 收到第一个异常时立即处理, 随后忽略所有异常 9 | * 10 | * @author Him188moe @ GUI Project 11 | */ 12 | public abstract class ExceptionConsumerOnce implements ExceptionConsumer { 13 | private boolean accepted = false; 14 | 15 | @SuppressWarnings("unchecked") 16 | @Override 17 | public void catchException(Player player, E exception) { 18 | if (!accepted) { 19 | accepted = true; 20 | E[] array = (E[]) Array.newInstance(exception.getClass(), 1); 21 | array[0] = exception; 22 | this.accept(player, array); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/InputFormatException.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import moe.him188.gui.template.element.TemplateElementInput; 4 | 5 | /** 6 | * 在表单元素 {@link TemplateElementInput} 中可能会出现的异常 7 | * 8 | * @author Him188moe @ GUI Project 9 | * @see InputType 10 | */ 11 | public class InputFormatException extends RuntimeException { 12 | private final Reason reason; 13 | private final String input; 14 | 15 | /** 16 | * @since 1.6 17 | */ 18 | public enum ReasonDefaults implements Reason { 19 | NUMBER_FORMAT, 20 | PLAYER_NOT_FOUND, 21 | LEVEL_NOT_FOUND, 22 | DATE_FORMAT, 23 | 24 | /** 25 | * @since 1.7 26 | */ 27 | USERNAME_FORMAT, 28 | } 29 | 30 | public interface Reason { 31 | 32 | } 33 | 34 | public InputFormatException(Reason reason, String originalInput) { 35 | super(); 36 | this.reason = reason; 37 | input = originalInput; 38 | } 39 | 40 | public InputFormatException(Reason reason, String originalInput, Throwable throwable) { 41 | super(throwable); 42 | this.input = originalInput; 43 | this.reason = reason; 44 | } 45 | 46 | public String getOriginalInput() { 47 | return input; 48 | } 49 | 50 | public Reason getReason() { 51 | return reason; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/InputType.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | /** 6 | * 输入数据类型. 7 | * 用于检查类型格式是否正确 8 | * 9 | * @author Him188moe @ GUI Project 10 | * @see InputTypes 11 | */ 12 | public abstract class InputType { 13 | 14 | /** 15 | * 检查内容是否符合规范 16 | */ 17 | @NotNull 18 | public abstract R parseResponse(String content) throws InputFormatException; 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/InputTypeBoolean.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | /** 6 | * @author Him188moe @ GUI Project 7 | */ 8 | public class InputTypeBoolean extends InputType { 9 | @NotNull 10 | @Override 11 | public Boolean parseResponse(String content) throws InputFormatException { 12 | switch (content.toLowerCase()) { 13 | case "1": 14 | case "yes": 15 | return true; 16 | } 17 | return Boolean.parseBoolean(content); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/InputTypeDate.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.text.DateFormat; 6 | import java.text.ParseException; 7 | import java.util.Date; 8 | 9 | /** 10 | * @author Him188moe @ GUI Project 11 | */ 12 | public class InputTypeDate extends InputType { 13 | private final DateFormat format; 14 | 15 | public InputTypeDate(DateFormat dateFormat) { 16 | format = dateFormat; 17 | } 18 | 19 | public DateFormat getFormat() { 20 | return format; 21 | } 22 | 23 | @NotNull 24 | @Override 25 | public Date parseResponse(String content) throws InputFormatException { 26 | try { 27 | return format.parse(content); 28 | } catch (ParseException e) { 29 | throw new InputFormatException(InputFormatException.ReasonDefaults.DATE_FORMAT, content, e); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/InputTypeDouble.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | /** 6 | * @author Him188moe @ GUI Project 7 | */ 8 | public class InputTypeDouble extends InputType { 9 | @NotNull 10 | @Override 11 | public Double parseResponse(String content) throws InputFormatException { 12 | try { 13 | return Double.parseDouble(content); 14 | } catch (NumberFormatException e) { 15 | throw new InputFormatException(InputFormatException.ReasonDefaults.NUMBER_FORMAT, content, e); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/InputTypeFloat.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | /** 6 | * @author Him188moe @ GUI Project 7 | */ 8 | public class InputTypeFloat extends InputType { 9 | @NotNull 10 | @Override 11 | public Float parseResponse(String content) throws InputFormatException { 12 | try { 13 | return (float) Double.parseDouble(content); 14 | } catch (NumberFormatException e) { 15 | throw new InputFormatException(InputFormatException.ReasonDefaults.NUMBER_FORMAT, content, e); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/InputTypeInteger.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | /** 6 | * @author Him188moe @ GUI Project 7 | */ 8 | public class InputTypeInteger extends InputType { 9 | @NotNull 10 | @Override 11 | public Integer parseResponse(String content) throws InputFormatException { 12 | try { 13 | return (int) Long.parseLong(content); 14 | } catch (NumberFormatException e) { 15 | throw new InputFormatException(InputFormatException.ReasonDefaults.NUMBER_FORMAT, content, e); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/InputTypeLevel.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import cn.nukkit.Server; 4 | import cn.nukkit.level.Level; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | /** 8 | * @author Him188moe @ GUI Project 9 | */ 10 | public class InputTypeLevel extends InputType { 11 | @NotNull 12 | @Override 13 | public Level parseResponse(String content) throws InputFormatException { 14 | Level level = Server.getInstance().getLevelByName(content); 15 | if (level == null) { 16 | throw new InputFormatException(InputFormatException.ReasonDefaults.LEVEL_NOT_FOUND, content, new NullPointerException()); 17 | } 18 | return level; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/InputTypeLong.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | /** 6 | * @author Him188moe @ GUI Project 7 | */ 8 | public class InputTypeLong extends InputType { 9 | @NotNull 10 | @Override 11 | public Long parseResponse(String content) throws InputFormatException { 12 | try { 13 | return Long.parseLong(content); 14 | } catch (NumberFormatException e) { 15 | throw new InputFormatException(InputFormatException.ReasonDefaults.NUMBER_FORMAT, content, e); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/InputTypePlayer.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.Server; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | /** 8 | * @author Him188moe @ GUI Project 9 | */ 10 | public class InputTypePlayer extends InputType { 11 | @NotNull 12 | @Override 13 | public Player parseResponse(String content) throws InputFormatException { 14 | Player player = Server.getInstance().getPlayer(content); 15 | if (player == null) { 16 | throw new InputFormatException(InputFormatException.ReasonDefaults.PLAYER_NOT_FOUND, content, new NullPointerException()); 17 | } 18 | 19 | return player; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/InputTypeString.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | /** 6 | * @author Him188moe @ GUI Project 7 | */ 8 | public class InputTypeString extends InputType { 9 | @NotNull 10 | @Override 11 | public String parseResponse(String content) throws InputFormatException { 12 | return content; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/InputTypeUsername.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import cn.nukkit.Player; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.util.Objects; 7 | 8 | /** 9 | * @author Him188moe @ GUI Project 10 | */ 11 | public class InputTypeUsername extends InputType { 12 | @NotNull 13 | @Override 14 | public String parseResponse(String content) throws InputFormatException { 15 | if (isValid(content)) { 16 | return content; 17 | } else { 18 | throw new InputFormatException(InputFormatException.ReasonDefaults.USERNAME_FORMAT, content); 19 | } 20 | } 21 | 22 | /** 23 | * Copied from nukkit 24 | * 25 | * @param username username 26 | * 27 | * @return valid or not 28 | * 29 | * @see Player#handleDataPacket loginPacket (at line 2100~) 30 | */ 31 | public static boolean isValid(String username) { 32 | int len = username.length(); 33 | if (len > 16 || len < 3) { 34 | return false; 35 | } 36 | 37 | for (int i = 0; i < len; i++) { 38 | char c = username.charAt(i); 39 | if ((c >= 'a' && c <= 'z') || 40 | (c >= 'A' && c <= 'Z') || 41 | (c >= '0' && c <= '9') || 42 | c == '_' || c == ' ' 43 | ) { 44 | continue; 45 | } 46 | 47 | return false; 48 | } 49 | 50 | return !Objects.equals(username, "rcon") && !Objects.equals(username, "console"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/InputTypes.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.level.Level; 5 | 6 | import java.text.DateFormat; 7 | import java.util.Date; 8 | 9 | /** 10 | * 默认的输入类型 11 | * 12 | * @author Him188moe @ GUI Project 13 | */ 14 | public final class InputTypes { 15 | /** 16 | * @since 1.7 17 | */ 18 | public static final InputType LONG = new InputTypeLong(); 19 | public static final InputType INTEGER = new InputTypeInteger(); 20 | public static final InputType DOUBLE = new InputTypeDouble(); 21 | /** 22 | * @since 1.7 23 | */ 24 | public static final InputType FLOAT = new InputTypeFloat(); 25 | /** 26 | * @since 1.7 27 | */ 28 | public static final InputType USERNAME = new InputTypeUsername(); 29 | public static final InputType STRING = new InputTypeString(); 30 | public static final InputType BOOLEAN = new InputTypeBoolean(); 31 | 32 | public static final InputType PLAYER = new InputTypePlayer(); 33 | public static final InputType LEVEL = new InputTypeLevel(); 34 | 35 | public static InputType DATE(DateFormat format) { 36 | return new InputTypeDate(format); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/KeyAlreadyContainsException.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import moe.him188.gui.template.element.TemplateElementInput; 4 | 5 | /** 6 | * 在表单元素 {@link TemplateElementInput} 中可能会出现的异常 7 | * 8 | * @author Him188moe @ GUI Project 9 | * @see InputType 10 | */ 11 | public class KeyAlreadyContainsException extends RuntimeException { 12 | public KeyAlreadyContainsException() { 13 | super(); 14 | } 15 | 16 | public KeyAlreadyContainsException(String message) { 17 | super(message); 18 | } 19 | 20 | public KeyAlreadyContainsException(String message, Throwable cause) { 21 | super(message, cause); 22 | } 23 | 24 | public KeyAlreadyContainsException(Throwable cause) { 25 | super(cause); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/NoParentWindowFoundException.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | /** 4 | * @author Him188moe @ GUI Project 5 | */ 6 | public class NoParentWindowFoundException extends RuntimeException { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/utils/ResponseParseException.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.utils; 2 | 3 | import moe.him188.gui.template.element.TemplateElement; 4 | import moe.him188.gui.template.element.TemplateElementDropdown; 5 | 6 | /** 7 | * @author Him188moe @ GUI Project 8 | */ 9 | public class ResponseParseException extends Exception { 10 | private final TemplateElement element; // TODO: 2018/8/2 0002 改进, 变得容易获取是哪一个项目异常 11 | 12 | public ResponseParseException(TemplateElement element, Throwable cause) { 13 | super(cause); 14 | this.element = element; 15 | } 16 | 17 | public TemplateElement getElement() { 18 | return element; 19 | } 20 | 21 | /** 22 | * 目前可能有以下异常 23 | *
    24 | *
  • {@link InputFormatException]}
  • 25 | *
26 | * 意外时(在 {@link TemplateElementDropdown} 中转换选中 ID)还可能出现 27 | *
    28 | *
  • {@link NumberFormatException]}
  • 29 | *
30 | * 31 | * @return Notnull 32 | * 33 | * @see InputFormatException 34 | */ 35 | @Override 36 | public synchronized Throwable getCause() { 37 | return super.getCause(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/FormCustom.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window; 2 | 3 | import cn.nukkit.form.element.Element; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Alias of the Responsible one 10 | * 11 | * @author Him188moe @ GUI project 12 | */ 13 | public class FormCustom extends ResponsibleFormWindowCustom { 14 | public FormCustom() { 15 | } 16 | 17 | public FormCustom(String title) { 18 | super(title); 19 | } 20 | 21 | public FormCustom(String title, Element... contents) { 22 | super(title, contents); 23 | } 24 | 25 | public FormCustom(String title, @NotNull List contents) { 26 | super(title, contents); 27 | } 28 | 29 | public FormCustom(String title, @NotNull List contents, @NotNull String icon) { 30 | super(title, contents, icon); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/FormModal.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window; 2 | 3 | /** 4 | * Alias of the Responsible one 5 | * 6 | * @author Him188moe @ GUI project 7 | */ 8 | public class FormModal extends ResponsibleFormWindowModal { 9 | public FormModal(String trueButtonText, String falseButtonText) { 10 | super(trueButtonText, falseButtonText); 11 | } 12 | 13 | public FormModal(String content, String trueButtonText, String falseButtonText) { 14 | super(content, trueButtonText, falseButtonText); 15 | } 16 | 17 | public FormModal(String title, String content, String trueButtonText, String falseButtonText) { 18 | super(title, content, trueButtonText, falseButtonText); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/FormSimple.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window; 2 | 3 | import cn.nukkit.form.element.ElementButton; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Alias of the Responsible one 10 | * 11 | * @author Him188moe @ GUI project 12 | */ 13 | public class FormSimple extends ResponsibleFormWindowSimple { 14 | public FormSimple() { 15 | } 16 | 17 | public FormSimple(String content) { 18 | super(content); 19 | } 20 | 21 | public FormSimple(String title, String content) { 22 | super(title, content); 23 | } 24 | 25 | public FormSimple(String title, String content, String... buttons) { 26 | super(title, content, buttons); 27 | } 28 | 29 | public FormSimple(String title, String content, ElementButton... buttons) { 30 | super(title, content, buttons); 31 | } 32 | 33 | public FormSimple(String title, String content, @NotNull List buttons) { 34 | super(title, content, buttons); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/FormSimpleAdvanced.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.Collection; 6 | import java.util.function.Function; 7 | 8 | /** 9 | * Alias of the Responsible one 10 | * 11 | * @author Him188moe @ GUI project 12 | */ 13 | public class FormSimpleAdvanced extends ResponsibleFormWindowSimpleAdvanced { 14 | 15 | public FormSimpleAdvanced(String title, String content, @NotNull Collection entries, @NotNull Function buttonTextGetter) { 16 | super(title, content, entries, buttonTextGetter); 17 | } 18 | 19 | public FormSimpleAdvanced(String content, @NotNull Collection entries, @NotNull Function buttonTextGetter) { 20 | super(content, entries, buttonTextGetter); 21 | } 22 | 23 | public FormSimpleAdvanced(@NotNull Collection entries, @NotNull Function buttonTextGetter) { 24 | super(entries, buttonTextGetter); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/FormSimpleMap.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.Map; 6 | import java.util.function.Function; 7 | 8 | /** 9 | * Alias of the Responsible one 10 | * 11 | * @author Him188moe @ GUI project 12 | */ 13 | public class FormSimpleMap extends ResponsibleFormWindowSimpleMap { 14 | public FormSimpleMap(String title, String content, @NotNull Map entries, @NotNull Function buttonTextGetter) { 15 | super(title, content, entries, buttonTextGetter); 16 | } 17 | 18 | public FormSimpleMap(String content, @NotNull Map entries, @NotNull Function buttonTextGetter) { 19 | super(content, entries, buttonTextGetter); 20 | } 21 | 22 | public FormSimpleMap(@NotNull Map entries, @NotNull Function buttonTextGetter) { 23 | super(entries, buttonTextGetter); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/FormTemplated.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window; 2 | 3 | import moe.him188.gui.template.Template; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | /** 7 | * Alias of the Responsible one 8 | * 9 | * @author Him188moe @ GUI project 10 | */ 11 | public class FormTemplated extends ResponsibleFormWindowTemplated { 12 | public FormTemplated(@NotNull Template template) { 13 | super(template); 14 | } 15 | 16 | public FormTemplated(String title, @NotNull Template template) { 17 | super(title, template); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/ResponsibleFormWindowCustom.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.event.Listener; 5 | import cn.nukkit.event.player.PlayerFormRespondedEvent; 6 | import cn.nukkit.form.element.Element; 7 | import cn.nukkit.form.response.FormResponse; 8 | import cn.nukkit.form.response.FormResponseCustom; 9 | import cn.nukkit.form.window.FormWindow; 10 | import cn.nukkit.form.window.FormWindowCustom; 11 | import com.google.gson.Gson; 12 | import moe.him188.gui.utils.Backable; 13 | import moe.him188.gui.window.listener.response.ResponseListenerCustom; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Arrays; 18 | import java.util.List; 19 | import java.util.Objects; 20 | import java.util.function.BiConsumer; 21 | import java.util.function.Consumer; 22 | 23 | /** 24 | * 能直接接受提交表单 ({@link #onResponded}) 和关闭窗口事件({@link #onClosed}) 的 {@link FormWindowCustom}. 25 | * 相较于 {@link FormWindowCustom}, 该类无需依赖于 {@link Listener} 去监听 {@link PlayerFormRespondedEvent}, 而可以直接通过 lambda 方法收到返回数据. 26 | *
27 | * {@link FormWindowCustom} that can receive click button event({@link #onResponded}) and close window event({@link #onClosed}). 28 | * Comparing with {@link FormWindowCustom}, this responsible one does not need {@link Listener} to listen {@link PlayerFormRespondedEvent}, 29 | * but it can directly receive {@link FormResponse} through lambda statements. 30 | * 31 | * @author Him188moe @ GUI Project 32 | */ 33 | public class ResponsibleFormWindowCustom extends FormWindowCustom implements Backable, ResponseListenerCustom { 34 | protected transient BiConsumer buttonClickedListener = null; 35 | 36 | protected transient Consumer windowClosedListener = null; 37 | 38 | private transient FormWindow parent = null; 39 | 40 | public ResponsibleFormWindowCustom() { 41 | this(""); 42 | } 43 | 44 | public ResponsibleFormWindowCustom(String title) { 45 | this(title, new ArrayList<>()); 46 | } 47 | 48 | public ResponsibleFormWindowCustom(String title, Element... contents) { 49 | this(title, Arrays.asList(contents)); 50 | } 51 | 52 | public ResponsibleFormWindowCustom(String title, @NotNull List contents) { 53 | this(title, contents, ""); 54 | } 55 | 56 | public ResponsibleFormWindowCustom(String title, @NotNull List contents, @NotNull String icon) { 57 | super(Objects.requireNonNull(title), Objects.requireNonNull(contents), Objects.requireNonNull(icon)); 58 | } 59 | 60 | @Override 61 | public void setParent(FormWindow parent) { 62 | this.parent = parent; 63 | } 64 | 65 | @Override 66 | public FormWindow getParent() { 67 | return this.parent; 68 | } 69 | 70 | /** 71 | * 在玩家提交表单后调用
72 | * Called on submitted 73 | * 74 | * @param listener 调用的方法 75 | */ 76 | public final ResponsibleFormWindowCustom onResponded(@NotNull BiConsumer listener) { 77 | Objects.requireNonNull(listener); 78 | this.buttonClickedListener = listener; 79 | return this; 80 | } 81 | 82 | /** 83 | * 在玩家提交表单后调用
84 | * Called on submitted 85 | * 86 | * @param listener 调用的方法(无 Player) 87 | */ 88 | public final ResponsibleFormWindowCustom onResponded(@NotNull Consumer listener) { 89 | Objects.requireNonNull(listener); 90 | this.buttonClickedListener = (response, player) -> listener.accept(response); 91 | return this; 92 | } 93 | 94 | /** 95 | * 在玩家提交表单后调用
96 | * Called on closed without submitting. 97 | * 98 | * @param listener 调用的方法(无参数) 99 | */ 100 | public final ResponsibleFormWindowCustom onResponded(@NotNull Runnable listener) { 101 | Objects.requireNonNull(listener); 102 | this.buttonClickedListener = (id, player) -> listener.run(); 103 | return this; 104 | } 105 | 106 | /** 107 | * 在玩家关闭窗口而没有点击按钮提交表单后调用.
108 | * Called on closed without submitting. 109 | * 110 | * @param listener 调用的方法 111 | */ 112 | public final ResponsibleFormWindowCustom onClosed(@NotNull Consumer listener) { 113 | Objects.requireNonNull(listener); 114 | this.windowClosedListener = listener; 115 | return this; 116 | } 117 | 118 | /** 119 | * 在玩家关闭窗口而没有点击按钮提交表单后调用.
120 | * Called on closed without submitting. 121 | * 122 | * @param listener 调用的方法 123 | */ 124 | public final ResponsibleFormWindowCustom onClosed(@NotNull Runnable listener) { 125 | Objects.requireNonNull(listener); 126 | this.windowClosedListener = (player) -> listener.run(); 127 | return this; 128 | } 129 | 130 | @Override 131 | public String getJSONData() { 132 | String toModify = new Gson().toJson(this, FormWindowCustom.class);//must!! 133 | //We need to replace this due to Java not supporting declaring class field 'default' 134 | return toModify.replace("defaultOptionIndex", "default") 135 | .replace("defaultText", "default") 136 | .replace("defaultValue", "default") 137 | .replace("defaultStepIndex", "default"); 138 | } 139 | 140 | public void callClicked(@NotNull FormResponseCustom response, @NotNull Player player) { 141 | Objects.requireNonNull(player); 142 | Objects.requireNonNull(response); 143 | 144 | this.onClicked(response, player); 145 | 146 | if (this.buttonClickedListener != null) { 147 | this.buttonClickedListener.accept(response, player); 148 | } 149 | } 150 | 151 | public void callClosed(@NotNull Player player) { 152 | Objects.requireNonNull(player); 153 | 154 | this.onClosed(player); 155 | 156 | if (this.windowClosedListener != null) { 157 | this.windowClosedListener.accept(player); 158 | } 159 | } 160 | 161 | static boolean onEvent(FormWindow formWindow, FormResponse response, Player player) { 162 | if (formWindow instanceof ResponsibleFormWindowCustom) { 163 | ResponsibleFormWindowCustom window = (ResponsibleFormWindowCustom) formWindow; 164 | 165 | if (window.wasClosed() || response == null) { 166 | window.callClosed(player); 167 | window.closed = false;//for resending 168 | } else { 169 | window.callClicked(((FormResponseCustom) response), player); 170 | } 171 | return true; 172 | } 173 | return false; 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/ResponsibleFormWindowModal.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.event.Listener; 5 | import cn.nukkit.event.player.PlayerFormRespondedEvent; 6 | import cn.nukkit.form.response.FormResponse; 7 | import cn.nukkit.form.response.FormResponseModal; 8 | import cn.nukkit.form.window.FormWindow; 9 | import cn.nukkit.form.window.FormWindowModal; 10 | import com.google.gson.Gson; 11 | import moe.him188.gui.utils.Backable; 12 | import moe.him188.gui.window.listener.response.ResponseListenerModal; 13 | import org.jetbrains.annotations.NotNull; 14 | import org.jetbrains.annotations.Nullable; 15 | 16 | import java.util.Objects; 17 | import java.util.function.BiConsumer; 18 | import java.util.function.Consumer; 19 | 20 | /** 21 | * 能直接接受提交表单 ({@link #onResponded}) 和关闭窗口事件({@link #onClosed}) 的 {@link FormWindowModal}. 22 | * 相较于 {@link FormWindowModal}, 该类无需依赖于 {@link Listener} 去监听 {@link PlayerFormRespondedEvent}, 而可以直接通过 lambda 方法收到返回数据. 23 | * 这个表单的组成是: 一段文字说明 + 两个按钮. 24 | *
25 | * {@link FormWindowModal} that can receive click button event({@link #onResponded}) and close window event({@link #onClosed}). 26 | * Comparing with {@link FormWindowModal}, this responsible one does not need {@link Listener} to listen {@link PlayerFormRespondedEvent}, 27 | * but it can directly receive {@link FormResponse} through lambda statements. 28 | * 29 | * @author Him188moe @ GUI Project 30 | */ 31 | public class ResponsibleFormWindowModal extends FormWindowModal implements Backable, ResponseListenerModal { 32 | protected transient BiConsumer buttonClickedListener = null; 33 | 34 | protected transient Consumer windowClosedListener = null; 35 | 36 | private transient FormWindow parent; 37 | 38 | public ResponsibleFormWindowModal(String trueButtonText, String falseButtonText) { 39 | super("", "", trueButtonText, falseButtonText); 40 | } 41 | 42 | public ResponsibleFormWindowModal(String content, String trueButtonText, String falseButtonText) { 43 | super("", content, trueButtonText, falseButtonText); 44 | } 45 | 46 | public ResponsibleFormWindowModal(String title, String content, String trueButtonText, String falseButtonText) { 47 | super(title, content, trueButtonText, falseButtonText); 48 | } 49 | 50 | @Override 51 | public void setParent(FormWindow parent) { 52 | this.parent = parent; 53 | } 54 | 55 | @Override 56 | public FormWindow getParent() { 57 | return parent; 58 | } 59 | 60 | /** 61 | * 在玩家提交表单, 或关闭表单窗口后调用.
62 | * Called on submitted 63 | * 64 | * @param listener 调用的方法 65 | */ 66 | public final ResponsibleFormWindowModal onResponded(@NotNull BiConsumer listener) { 67 | Objects.requireNonNull(listener); 68 | this.buttonClickedListener = listener; 69 | return this; 70 | } 71 | 72 | /** 73 | * 在玩家提交表单后调用.
74 | * Called on submitted 75 | * 76 | * @param listener 调用的方法(无 Player) 77 | */ 78 | public final ResponsibleFormWindowModal onResponded(@NotNull Consumer listener) { 79 | Objects.requireNonNull(listener); 80 | this.buttonClickedListener = (response, player) -> listener.accept(response); 81 | return this; 82 | } 83 | 84 | /** 85 | * 在玩家提交表单后调用
86 | * Called on submitted 87 | * 88 | * @param listener 调用的方法(无参数) 89 | */ 90 | public final ResponsibleFormWindowModal onResponded(@NotNull Runnable listener) { 91 | Objects.requireNonNull(listener); 92 | this.buttonClickedListener = (id, player) -> listener.run(); 93 | return this; 94 | } 95 | 96 | /** 97 | * 在玩家提交表单后调用
98 | * Called on submitted 99 | * 100 | * @param listenerOnTrue 当玩家点击 true 按钮时调用的函数 101 | * @param listenerOnFalse 当玩家点击 false 按钮时调用的函数 102 | */ 103 | public final ResponsibleFormWindowModal onResponded(@NotNull Runnable listenerOnTrue, @NotNull Runnable listenerOnFalse) { 104 | Objects.requireNonNull(listenerOnTrue); 105 | Objects.requireNonNull(listenerOnFalse); 106 | this.buttonClickedListener = (confirmation, player) -> { 107 | if (confirmation) { 108 | listenerOnTrue.run(); 109 | } else { 110 | listenerOnFalse.run(); 111 | } 112 | }; 113 | return this; 114 | } 115 | 116 | /** 117 | * 在玩家提交表单后调用
118 | * Called on submitted
119 | * 参数任意一项可以为 null.
120 | * Args can be null. 121 | * 122 | * @param listenerOnTrue 当玩家点击 true 按钮时调用的函数 123 | * @param listenerOnFalse 当玩家点击 false 按钮时调用的函数 124 | */ 125 | public final ResponsibleFormWindowModal onResponded(@Nullable Consumer listenerOnTrue, @Nullable Consumer listenerOnFalse) { 126 | this.buttonClickedListener = (confirmation, player) -> { 127 | if (confirmation) { 128 | if (listenerOnTrue != null) { 129 | listenerOnTrue.accept(player); 130 | } 131 | } else { 132 | if (listenerOnFalse != null) { 133 | listenerOnFalse.accept(player); 134 | } 135 | } 136 | }; 137 | return this; 138 | } 139 | 140 | /** 141 | * 在玩家关闭窗口而没有点击按钮提交表单后调用.
142 | * Called on closed without submitting. 143 | * 144 | * @param listener 调用的方法 145 | */ 146 | public final ResponsibleFormWindowModal onClosed(@NotNull Consumer listener) { 147 | Objects.requireNonNull(listener); 148 | this.windowClosedListener = listener; 149 | return this; 150 | } 151 | 152 | /** 153 | * 在玩家关闭窗口而没有点击按钮提交表单后调用.
154 | * Called on closed without submitting. 155 | * 156 | * @param listener 调用的方法 157 | */ 158 | public final ResponsibleFormWindowModal onClosed(@NotNull Runnable listener) { 159 | Objects.requireNonNull(listener); 160 | this.windowClosedListener = (player) -> listener.run(); 161 | return this; 162 | } 163 | 164 | @Override 165 | public String getJSONData() { 166 | return new Gson().toJson(this, FormWindowModal.class); 167 | } 168 | 169 | public void callClicked(boolean response, @NotNull Player player) { 170 | Objects.requireNonNull(player); 171 | 172 | this.onClicked(response, player); 173 | 174 | if (this.buttonClickedListener != null) { 175 | this.buttonClickedListener.accept(response, Objects.requireNonNull(player)); 176 | } 177 | } 178 | 179 | public void callClosed(@NotNull Player player) { 180 | Objects.requireNonNull(player); 181 | 182 | this.onClosed(player); 183 | 184 | if (this.windowClosedListener != null) { 185 | this.windowClosedListener.accept(Objects.requireNonNull(player)); 186 | } 187 | } 188 | 189 | @SuppressWarnings("UnusedReturnValue") 190 | static boolean onEvent(FormWindow formWindow, FormResponse response, Player player) { 191 | if (formWindow instanceof ResponsibleFormWindowModal) { 192 | ResponsibleFormWindowModal window = (ResponsibleFormWindowModal) formWindow; 193 | 194 | if (window.wasClosed() || response == null) { 195 | window.callClosed(player); 196 | window.closed = false;//for resending 197 | } else { 198 | window.callClicked(((FormResponseModal) response).getClickedButtonId() == 0, player); 199 | } 200 | return true; 201 | } 202 | return false; 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/ResponsibleFormWindowSimple.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.event.Listener; 5 | import cn.nukkit.event.player.PlayerFormRespondedEvent; 6 | import cn.nukkit.form.element.ElementButton; 7 | import cn.nukkit.form.response.FormResponse; 8 | import cn.nukkit.form.response.FormResponseSimple; 9 | import cn.nukkit.form.window.FormWindow; 10 | import cn.nukkit.form.window.FormWindowSimple; 11 | import com.google.gson.Gson; 12 | import moe.him188.gui.element.ResponsibleButton; 13 | import moe.him188.gui.utils.Backable; 14 | import moe.him188.gui.window.listener.action.ClickListener; 15 | import moe.him188.gui.window.listener.action.ClickListenerSimple; 16 | import moe.him188.gui.window.listener.response.ResponseListenerSimple; 17 | import org.jetbrains.annotations.NotNull; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Arrays; 21 | import java.util.List; 22 | import java.util.Objects; 23 | import java.util.function.BiConsumer; 24 | import java.util.function.Consumer; 25 | import java.util.stream.Collectors; 26 | 27 | /** 28 | * 能直接接受提交表单 ({@link #onClicked} 和关闭窗口事件({@link #onClosed}) 的 {@link FormWindowSimple}. 29 | * 相较于 {@link FormWindowSimple}, 该类无需依赖于 {@link Listener} 去监听 {@link PlayerFormRespondedEvent}, 而可以直接通过 lambda 方法收到返回数据. 30 | * 这个表单的组成是: 一段文字说明 + 很多个按钮. 按钮之间不能插入另一段文字说明. 31 | *
32 | * {@link FormWindowSimple} that can receive click button event({@link #onClicked}) and close window event({@link #onClosed}). 33 | * Comparing with {@link FormWindowSimple}, this responsible one does not need {@link Listener} to listen {@link PlayerFormRespondedEvent}, 34 | * but it can directly receive {@link FormResponse} through lambda statements. 35 | * The composition of the form is: A piece of message + button(s). There are no more messages between buttons. 36 | * 37 | * @author Him188moe @ GUI Project 38 | */ 39 | public class ResponsibleFormWindowSimple extends FormWindowSimple implements Backable, ResponseListenerSimple { 40 | protected transient BiConsumer buttonClickedListener = null; 41 | 42 | protected transient Consumer windowClosedListener = null; 43 | 44 | private transient FormWindow parent; 45 | 46 | public ResponsibleFormWindowSimple() { 47 | this("", "", new ArrayList<>()); 48 | } 49 | 50 | public ResponsibleFormWindowSimple(String content) { 51 | this("", content, new ArrayList<>()); 52 | } 53 | 54 | public ResponsibleFormWindowSimple(String title, String content) { 55 | this(title, content, new ArrayList<>()); 56 | } 57 | 58 | public ResponsibleFormWindowSimple(String title, String content, String... buttons) { 59 | super(title, content, Arrays.stream(buttons).map(ElementButton::new).collect(Collectors.toList())); 60 | } 61 | 62 | public ResponsibleFormWindowSimple(String title, String content, ElementButton... buttons) { 63 | super(title, content, Arrays.asList(buttons)); 64 | } 65 | 66 | public ResponsibleFormWindowSimple(String title, String content, @NotNull List buttons) { 67 | super(Objects.requireNonNull(title), Objects.requireNonNull(content), Objects.requireNonNull(buttons)); 68 | } 69 | 70 | @Override 71 | public void setParent(FormWindow parent) { 72 | this.parent = parent; 73 | } 74 | 75 | @Override 76 | public FormWindow getParent() { 77 | return parent; 78 | } 79 | 80 | /** 81 | * 在玩家提交表单后调用
82 | * Called on submitted 83 | * 84 | * @param listener 调用的方法 85 | */ 86 | public final ResponsibleFormWindowSimple onClicked(@NotNull BiConsumer listener) { 87 | Objects.requireNonNull(listener); 88 | this.buttonClickedListener = listener; 89 | return this; 90 | } 91 | 92 | /** 93 | * 在玩家提交表单后调用
94 | * Called on submitted 95 | * 96 | * @param listener 调用的方法(无 Player) 97 | */ 98 | public final ResponsibleFormWindowSimple onClicked(@NotNull Consumer listener) { 99 | Objects.requireNonNull(listener); 100 | this.buttonClickedListener = (id, player) -> listener.accept(id); 101 | return this; 102 | } 103 | 104 | /** 105 | * 在玩家提交表单后调用
106 | * Called on submitted 107 | * 108 | * @param listener 调用的方法(无参数) 109 | */ 110 | public final ResponsibleFormWindowSimple onClicked(@NotNull Runnable listener) { 111 | Objects.requireNonNull(listener); 112 | this.buttonClickedListener = (id, player) -> listener.run(); 113 | return this; 114 | } 115 | 116 | 117 | /** 118 | * 在玩家关闭窗口而没有点击按钮提交表单后调用.
119 | * Called on submitted 120 | * 121 | * @param listener 调用的方法 122 | */ 123 | public final ResponsibleFormWindowSimple onClosed(@NotNull Consumer listener) { 124 | Objects.requireNonNull(listener); 125 | this.windowClosedListener = listener; 126 | return this; 127 | } 128 | 129 | /** 130 | * 在玩家关闭窗口而没有点击按钮提交表单后调用.
131 | * Called on closed without submitting. 132 | * 133 | * @param listener 调用的方法 134 | */ 135 | public final ResponsibleFormWindowSimple onClosed(@NotNull Runnable listener) { 136 | Objects.requireNonNull(listener); 137 | this.windowClosedListener = (player) -> listener.run(); 138 | return this; 139 | } 140 | 141 | /** 142 | * 快速添加 {@link ElementButton}.
143 | * Fast adding {@link ElementButton} 144 | * 145 | * @param name button name 146 | */ 147 | public void addButton(String name) { 148 | super.addButton(new ElementButton(name)); 149 | } 150 | 151 | /** 152 | * 快速添加 {@link ResponsibleButton}.
153 | * Fast adding {@link ResponsibleButton} 154 | * 155 | * @param name button name 156 | * @param clickListener listener 157 | */ 158 | public void addButton(String name, @NotNull ClickListener clickListener) { 159 | super.addButton(new ResponsibleButton(name, clickListener)); 160 | } 161 | 162 | /** 163 | * 快速添加 {@link ResponsibleButton}.
164 | * Fast adding {@link ResponsibleButton} 165 | * 166 | * @param name button name 167 | * @param clickListener listener 168 | */ 169 | public void addButton(String name, @NotNull ClickListenerSimple clickListener) { 170 | super.addButton(new ResponsibleButton(name, clickListener)); 171 | } 172 | 173 | @Override 174 | public String getJSONData() { 175 | return new Gson().toJson(this, FormWindowSimple.class); 176 | } 177 | 178 | public void callClicked(int id, @NotNull Player player) { 179 | Objects.requireNonNull(player); 180 | 181 | ElementButton button = getButtons().get(id); 182 | if (button instanceof ResponsibleButton) { 183 | ((ResponsibleButton) button).callClicked(player); 184 | } 185 | 186 | this.onClicked(id, player); 187 | 188 | if (this.buttonClickedListener != null) { 189 | this.buttonClickedListener.accept(id, player); 190 | } 191 | } 192 | 193 | public void callClosed(Player player) { 194 | Objects.requireNonNull(player); 195 | 196 | this.onClosed(player); 197 | 198 | if (this.windowClosedListener != null) { 199 | this.windowClosedListener.accept(player); 200 | } 201 | } 202 | 203 | static boolean onEvent(FormWindow formWindow, FormResponse response, Player player) { 204 | if (formWindow instanceof ResponsibleFormWindowSimple) { 205 | ResponsibleFormWindowSimple window = (ResponsibleFormWindowSimple) formWindow; 206 | 207 | if (window.wasClosed() || response == null) { 208 | window.callClosed(player); 209 | window.closed = false;//for resending 210 | } else { 211 | window.callClicked(((FormResponseSimple) response).getClickedButtonId(), player); 212 | } 213 | return true; 214 | } 215 | return false; 216 | } 217 | } -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/ResponsibleFormWindowSimpleAdvanced.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.form.element.ElementButton; 5 | import cn.nukkit.form.response.FormResponse; 6 | import cn.nukkit.form.response.FormResponseSimple; 7 | import cn.nukkit.form.window.FormWindow; 8 | import cn.nukkit.form.window.FormWindowSimple; 9 | import com.google.gson.Gson; 10 | import moe.him188.gui.utils.Backable; 11 | import moe.him188.gui.utils.NoParentWindowFoundException; 12 | import moe.him188.gui.window.listener.response.ResponseListenerAdvanced; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.*; 16 | import java.util.function.BiConsumer; 17 | import java.util.function.Consumer; 18 | import java.util.function.Function; 19 | 20 | /** 21 | * {@link ResponsibleFormWindowSimple} 高级版(2333).
22 | * Advanced {@link ResponsibleFormWindowSimple}
23 | * 高级版无需通过 id 自找数据, 一切的一切都由 GUI 帮你完成. 你只需要实现后续处理即可!
24 | * You needn't get entries from id that is responded in {@link ResponsibleFormWindowSimple#onClicked}, but everything will be done by GUI.
25 | *
26 | * 普通版里面, 点击事件函数参数1是 int, 是按钮的 ID.
27 | * In Normal, type of click event function parameter 1 is int, which represents the button ID.
28 | * 并且需要使用 Simple 类型表单的情况, 一般是有一组同一类型的数据需要依次显示. 比如传送世界列表, 任务列表
29 | * And when you are using the {@link FormWindowSimple}, generally it's time of a group of the same type of data need to show. Such as teleport positions, missions lists.
30 | * 在普通版构造器中, 你需要手动对数据遍历并添加按钮, 在普通版click事件中, 你又需要手动从数据({@link List},{@link Map}等)中取出第 n 项, 再处理.
31 | * In normal constructors, you should foreach your Collections add add buttons.
32 | * In normal click functions, you should get entry(value) from Collections.
33 | * 如果再来一个窗口, 你又要枯燥地重复一切. 那么是时候使用高级版了!
34 | * In another window, you need to repeat everything again, that's too boring! At this time, you should use Advanced.
35 | *
36 | * 高级版里面, 由java泛型支持, 你在构造器中需要传入一组数据和构造按钮的函数, 然后在点击事件中直接拿到按钮对应的数据进行处理
37 | * In Advanced, with the support of java Generics, you can get entry directly in click events.
38 | *

39 | * 普通版要这么做:
40 | * In Normal:
41 | *

 42 |  * List data = new ArrayList<>();
 43 |  * window = new 普通版(title, content){
 44 |  *     for (publisher: data){
 45 |  *         addButton(new ElementButton(publisher.getName()));
 46 |  *     }
 47 |  *
 48 |  *     onClicked((id, player) -> {
 49 |  *         Publisher publisher = data.get(id);
 50 |  *         //coding
 51 |  *     })
 52 |  * };
 53 |  * 
54 | * 高级版只要这么做:
55 | * In Advanced:
56 | *
 57 |  * window = new 高级版(title, content, data, Publisher::getName).onClicked((publisher, player) -> {
 58 |  *     //coding
 59 |  * });
 60 |  * 
61 | * 62 | * @author Him188moe @ GUI Project 63 | */ 64 | public class ResponsibleFormWindowSimpleAdvanced extends FormWindowSimple implements Backable, ResponseListenerAdvanced { 65 | protected transient BiConsumer buttonClickedListener = null; 66 | 67 | protected transient Consumer windowClosedListener = null; 68 | 69 | protected transient final List entries; 70 | 71 | private transient FormWindow parent; 72 | 73 | /** 74 | * @param title 标题 75 | * @param content 内容 76 | * @param entries 需要展示在每个按钮上的数据 | entries to show in the buttons 77 | * @param buttonTextGetter 按钮名字获取器. 用于获取每个数据对应的按钮的名字 | Used to get the name of each button 78 | */ 79 | public ResponsibleFormWindowSimpleAdvanced(String title, String content, @NotNull Collection entries, @NotNull Function buttonTextGetter) { 80 | super(Objects.requireNonNull(title), Objects.requireNonNull(content)); 81 | Objects.requireNonNull(buttonTextGetter); 82 | Objects.requireNonNull(entries); 83 | 84 | for (E entry : entries) { 85 | this.addButton(new ElementButton(buttonTextGetter.apply(entry))); 86 | } 87 | this.entries = Collections.unmodifiableList(new ArrayList<>(entries)); 88 | } 89 | 90 | /** 91 | * @param content 内容 92 | * @param entries 需要展示在每个按钮上的数据 | entries to show in the buttons 93 | * @param buttonTextGetter 按钮名字获取器. 用于获取每个数据对应的按钮的名字 | Used to get the name of each button 94 | */ 95 | public ResponsibleFormWindowSimpleAdvanced(String content, @NotNull Collection entries, @NotNull Function buttonTextGetter) { 96 | this("", content, entries, buttonTextGetter); 97 | } 98 | 99 | /** 100 | * @param entries 需要展示在每个按钮上的数据 | entries to show in the buttons 101 | * @param buttonTextGetter 按钮名字获取器. 用于获取每个数据对应的按钮的名字 | Used to get the name of each button 102 | */ 103 | public ResponsibleFormWindowSimpleAdvanced(@NotNull Collection entries, @NotNull Function buttonTextGetter) { 104 | this("", "", entries, buttonTextGetter); 105 | } 106 | 107 | public List getEntries() { 108 | return entries; 109 | } 110 | 111 | public E getEntry(int id) { 112 | return this.entries.get(id); 113 | } 114 | 115 | @Override 116 | public void setParent(FormWindow parent) throws NoParentWindowFoundException { 117 | this.parent = parent; 118 | } 119 | 120 | @Override 121 | public FormWindow getParent() { 122 | return parent; 123 | } 124 | 125 | /** 126 | * 在玩家提交表单后调用
127 | * Called on submitted 128 | * 129 | * @param listener 调用的方法 130 | */ 131 | public final ResponsibleFormWindowSimpleAdvanced onClicked(@NotNull BiConsumer listener) { 132 | Objects.requireNonNull(listener); 133 | this.buttonClickedListener = listener; 134 | return this; 135 | } 136 | 137 | /** 138 | * 在玩家提交表单后调用
139 | * Called on submitted 140 | * 141 | * @param listener 调用的方法(无 Player) 142 | */ 143 | public final ResponsibleFormWindowSimpleAdvanced onClicked(@NotNull Consumer listener) { 144 | Objects.requireNonNull(listener); 145 | this.buttonClickedListener = (response, player) -> listener.accept(response); 146 | return this; 147 | } 148 | 149 | /** 150 | * 在玩家关闭窗口而没有点击按钮提交表单后调用.
151 | * Called on closed without submitting. 152 | * 153 | * @param listener 调用的方法 154 | */ 155 | public final ResponsibleFormWindowSimpleAdvanced onClosed(@NotNull Consumer listener) { 156 | Objects.requireNonNull(listener); 157 | this.windowClosedListener = listener; 158 | return this; 159 | } 160 | 161 | /** 162 | * 在玩家关闭窗口而没有点击按钮提交表单后调用.
163 | * Called on closed without submitting. 164 | * 165 | * @param listener 调用的方法 166 | */ 167 | public final ResponsibleFormWindowSimpleAdvanced onClosed(@NotNull Runnable listener) { 168 | Objects.requireNonNull(listener); 169 | this.windowClosedListener = (player) -> listener.run(); 170 | return this; 171 | } 172 | 173 | @Override 174 | public void addButton(@NotNull ElementButton button) { 175 | Objects.requireNonNull(button); 176 | if (this.entries != null) { 177 | throw new UnsupportedOperationException("could not addButton after construction!"); 178 | } 179 | super.addButton(button); 180 | } 181 | 182 | @SuppressWarnings("unchecked") 183 | public void callClicked(@NotNull E entry, @NotNull Player player) { 184 | Objects.requireNonNull(player); 185 | Objects.requireNonNull(entry); 186 | 187 | this.onClicked(entry, player); 188 | 189 | if (this.buttonClickedListener != null) { 190 | this.buttonClickedListener.accept(entry, player); 191 | } 192 | } 193 | 194 | public void callClosed(@NotNull Player player) { 195 | Objects.requireNonNull(player); 196 | 197 | this.onClosed(player); 198 | 199 | if (this.windowClosedListener != null) { 200 | this.windowClosedListener.accept(player); 201 | } 202 | } 203 | 204 | @Override 205 | public String getJSONData() { 206 | return new Gson().toJson(this, FormWindowSimple.class); //必须以无泛型类转换 json, 否则 StackOverFollow 207 | } 208 | 209 | @SuppressWarnings("unchecked") 210 | static boolean onEvent(FormWindow formWindow, FormResponse response, Player player) { 211 | if (formWindow instanceof ResponsibleFormWindowSimpleAdvanced) { 212 | ResponsibleFormWindowSimpleAdvanced window = (ResponsibleFormWindowSimpleAdvanced) formWindow; 213 | 214 | if (window.wasClosed() || response == null) { 215 | window.callClosed(player); 216 | window.closed = false;//for resending 217 | } else { 218 | window.callClicked(window.getEntry(((FormResponseSimple) response).getClickedButtonId()), player); 219 | } 220 | return true; 221 | } 222 | return false; 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/ResponsibleFormWindowSimpleMap.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.form.element.ElementButton; 5 | import cn.nukkit.form.response.FormResponse; 6 | import cn.nukkit.form.response.FormResponseSimple; 7 | import cn.nukkit.form.window.FormWindow; 8 | import cn.nukkit.form.window.FormWindowSimple; 9 | import com.google.gson.Gson; 10 | import moe.him188.gui.utils.Backable; 11 | import moe.him188.gui.utils.NoParentWindowFoundException; 12 | import moe.him188.gui.window.listener.response.ResponseListenerAdvanced; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.*; 16 | import java.util.function.BiConsumer; 17 | import java.util.function.Consumer; 18 | import java.util.function.Function; 19 | 20 | /** 21 | * 类似于 {@link ResponsibleFormWindowSimpleAdvanced}. 22 | * 23 | * @author Him188moe @ GUI Project 24 | * @since 1.8 25 | */ 26 | public class ResponsibleFormWindowSimpleMap extends FormWindowSimple implements Backable, ResponseListenerAdvanced { 27 | protected transient BiConsumer buttonClickedListener = null; 28 | 29 | protected transient Consumer windowClosedListener = null; 30 | 31 | protected transient final Map entries; 32 | 33 | protected transient final List values; 34 | 35 | private transient FormWindow parent; 36 | 37 | /** 38 | * @param title 标题 39 | * @param content 内容 40 | * @param entries 需要展示在每个按钮上的数据 | entries to show in the buttons 41 | * @param buttonTextGetter 按钮名字获取器. 用于获取每个数据对应的按钮的名字. (通过 map 的 key) | Used to get the name of each button (By map key) 42 | */ 43 | public ResponsibleFormWindowSimpleMap(String title, String content, @NotNull Map entries, @NotNull Function buttonTextGetter) { 44 | super(Objects.requireNonNull(title), Objects.requireNonNull(content)); 45 | Objects.requireNonNull(buttonTextGetter); 46 | Objects.requireNonNull(entries); 47 | 48 | for (K entry : entries.keySet()) { 49 | this.addButton(new ElementButton(buttonTextGetter.apply(entry))); 50 | } 51 | this.values = Collections.unmodifiableList(new ArrayList<>(entries.values())); 52 | 53 | this.entries = Collections.unmodifiableMap(entries); 54 | } 55 | 56 | /** 57 | * @param content 内容 58 | * @param entries 需要展示在每个按钮上的数据 | entries to show in the buttons 59 | * @param buttonTextGetter 按钮名字获取器. 用于获取每个数据对应的按钮的名字. (通过 map 的 key) | Used to get the name of each button (By map key) 60 | */ 61 | public ResponsibleFormWindowSimpleMap(String content, @NotNull Map entries, @NotNull Function buttonTextGetter) { 62 | this("", content, entries, buttonTextGetter); 63 | } 64 | 65 | /** 66 | * @param entries 需要展示在每个按钮上的数据 | entries to show in the buttons 67 | * @param buttonTextGetter 按钮名字获取器. 用于获取每个数据对应的按钮的名字. (通过 map 的 key) | Used to get the name of each button (By map key) 68 | */ 69 | public ResponsibleFormWindowSimpleMap(@NotNull Map entries, @NotNull Function buttonTextGetter) { 70 | this("", "", entries, buttonTextGetter); 71 | } 72 | 73 | public Map getEntries() { 74 | return entries; 75 | } 76 | 77 | public V getEntryValue(int id) { 78 | return this.values.get(id); 79 | } 80 | 81 | @Override 82 | public void setParent(FormWindow parent) throws NoParentWindowFoundException { 83 | this.parent = parent; 84 | } 85 | 86 | @Override 87 | public FormWindow getParent() { 88 | return parent; 89 | } 90 | 91 | /** 92 | * 在玩家提交表单后调用
93 | * Called on submitted 94 | * 95 | * @param listener 调用的方法 96 | */ 97 | public final ResponsibleFormWindowSimpleMap onClicked(@NotNull BiConsumer listener) { 98 | Objects.requireNonNull(listener); 99 | this.buttonClickedListener = listener; 100 | return this; 101 | } 102 | 103 | /** 104 | * 在玩家提交表单后调用
105 | * Called on submitted 106 | * 107 | * @param listener 调用的方法(无 Player) 108 | */ 109 | public final ResponsibleFormWindowSimpleMap onClicked(@NotNull Consumer listener) { 110 | Objects.requireNonNull(listener); 111 | this.buttonClickedListener = (response, player) -> listener.accept(response); 112 | return this; 113 | } 114 | 115 | /** 116 | * 在玩家关闭窗口而没有点击按钮提交表单后调用.
117 | * Called on closed without submitting. 118 | * 119 | * @param listener 调用的方法 120 | */ 121 | public final ResponsibleFormWindowSimpleMap onClosed(@NotNull Consumer listener) { 122 | Objects.requireNonNull(listener); 123 | this.windowClosedListener = listener; 124 | return this; 125 | } 126 | 127 | /** 128 | * 在玩家关闭窗口而没有点击按钮提交表单后调用.
129 | * Called on closed without submitting. 130 | * 131 | * @param listener 调用的方法 132 | */ 133 | public final ResponsibleFormWindowSimpleMap onClosed(@NotNull Runnable listener) { 134 | Objects.requireNonNull(listener); 135 | this.windowClosedListener = (player) -> listener.run(); 136 | return this; 137 | } 138 | 139 | @Override 140 | public void addButton(@NotNull ElementButton button) { 141 | Objects.requireNonNull(button); 142 | if (this.entries != null) { 143 | throw new UnsupportedOperationException("could not addButton after construction!"); 144 | } 145 | super.addButton(button); 146 | } 147 | 148 | @SuppressWarnings("unchecked") 149 | public void callClicked(@NotNull V entry, @NotNull Player player) { 150 | Objects.requireNonNull(player); 151 | Objects.requireNonNull(entry); 152 | 153 | this.onClicked(entry, player); 154 | 155 | if (this.buttonClickedListener != null) { 156 | this.buttonClickedListener.accept(entry, player); 157 | } 158 | } 159 | 160 | public void callClosed(@NotNull Player player) { 161 | Objects.requireNonNull(player); 162 | 163 | this.onClosed(player); 164 | 165 | if (this.windowClosedListener != null) { 166 | this.windowClosedListener.accept(player); 167 | } 168 | } 169 | 170 | @Override 171 | public String getJSONData() { 172 | return new Gson().toJson(this, FormWindowSimple.class); //必须以无泛型类转换 json, 否则 StackOverFollow 173 | } 174 | 175 | @SuppressWarnings("unchecked") 176 | static boolean onEvent(FormWindow formWindow, FormResponse response, Player player) { 177 | if (formWindow instanceof ResponsibleFormWindowSimpleMap) { 178 | ResponsibleFormWindowSimpleMap window = (ResponsibleFormWindowSimpleMap) formWindow; 179 | 180 | if (window.wasClosed() || response == null) { 181 | window.callClosed(player); 182 | window.closed = false;//for resending 183 | } else { 184 | window.callClicked(window.getEntryValue(((FormResponseSimple) response).getClickedButtonId()), player); 185 | } 186 | return true; 187 | } 188 | return false; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/ResponsibleFormWindowTemplated.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.event.Listener; 5 | import cn.nukkit.event.player.PlayerFormRespondedEvent; 6 | import cn.nukkit.form.response.FormResponse; 7 | import cn.nukkit.form.response.FormResponseCustom; 8 | import cn.nukkit.form.window.FormWindow; 9 | import cn.nukkit.form.window.FormWindowCustom; 10 | import com.google.gson.Gson; 11 | import moe.him188.gui.template.Template; 12 | import moe.him188.gui.template.response.TemplateResponses; 13 | import moe.him188.gui.utils.Backable; 14 | import moe.him188.gui.utils.ExceptionConsumer; 15 | import moe.him188.gui.utils.NoParentWindowFoundException; 16 | import moe.him188.gui.utils.ResponseParseException; 17 | import moe.him188.gui.window.listener.response.ResponseListenerTemplate; 18 | import org.jetbrains.annotations.NotNull; 19 | import org.jetbrains.annotations.Nullable; 20 | 21 | import java.util.ArrayList; 22 | import java.util.Objects; 23 | import java.util.function.BiConsumer; 24 | import java.util.function.Consumer; 25 | 26 | /** 27 | * 能直接接受提交表单 ({@link #onResponded}) 和关闭窗口事件({@link #onClosed}) 的 {@link FormWindowCustom}. 28 | * 相较于 {@link FormWindowCustom}, 该类无需依赖于 {@link Listener} 去监听 {@link PlayerFormRespondedEvent}, 而可以直接通过 lambda 方法收到返回数据. 29 | *
30 | * {@link FormWindowCustom} that can receive click button event({@link #onResponded}) and close window event({@link #onClosed}). 31 | * Comparing with {@link FormWindowCustom}, this responsible one does not need {@link Listener} to listen {@link PlayerFormRespondedEvent}, 32 | * but it can directly receive {@link FormResponse} through lambda statements. 33 | * 34 | * @author Him188moe @ GUI Project 35 | */ 36 | public class ResponsibleFormWindowTemplated extends FormWindowCustom implements Backable, ResponseListenerTemplate { 37 | transient final Template template; 38 | 39 | protected transient BiConsumer, Player> buttonClickedListener = null; 40 | 41 | protected transient Consumer windowClosedListener = null; 42 | 43 | protected transient TemplateResponses lastResponses = null; 44 | 45 | protected transient Player lastPlayer = null; 46 | 47 | @Nullable 48 | protected transient ExceptionConsumer exceptionConsumer; 49 | 50 | private transient FormWindow parent; 51 | 52 | public ResponsibleFormWindowTemplated(@NotNull Template template) { 53 | this("", template); 54 | } 55 | 56 | public ResponsibleFormWindowTemplated(String title, @NotNull Template template) { 57 | super(Objects.requireNonNull(title), new ArrayList<>(), ""); 58 | Objects.requireNonNull(template); 59 | this.template = template; 60 | template.getBuilder().applyTemplateTo(this); 61 | } 62 | 63 | public Template getTemplate() { 64 | return template; 65 | } 66 | 67 | @Override 68 | public void setParent(FormWindow parent) { 69 | this.parent = parent; 70 | } 71 | 72 | @Override 73 | public FormWindow getParent() { 74 | return this.parent; 75 | } 76 | 77 | @Override 78 | public void goBack(Player player) throws NoParentWindowFoundException { 79 | Objects.requireNonNull(player); 80 | if (getParent() == null) { 81 | throw new NoParentWindowFoundException(); 82 | } 83 | if (this.lastResponses != null) { 84 | this.applyLastResponse(); 85 | } 86 | player.showFormWindow(this.getParent()); 87 | } 88 | 89 | /** 90 | * Get last responses 91 | * 92 | * @return last response, nullable. 93 | * @see #setLastResponses(Player, FormResponseCustom) 94 | * @see #onEvent(FormWindow, FormResponse, Player) 95 | */ 96 | public TemplateResponses getLastResponses() { 97 | return lastResponses; 98 | } 99 | 100 | /** 101 | * Apply {@link #lastResponses} to this. 102 | * 103 | * @see TemplateResponses.Builder#applyToWindow(FormWindowCustom, K[]) 104 | */ 105 | public void applyLastResponse() throws NullPointerException { 106 | this.applyLastResponse(null); 107 | } 108 | 109 | /** 110 | * Apply {@link #lastResponses} to this. 111 | * 112 | * @param doNotKeepValues keys that will not be filled. These elements will be defaultValues 113 | * 114 | * @see TemplateResponses.Builder#applyToWindow(FormWindowCustom, K[]) 115 | */ 116 | public void applyLastResponse(@Nullable K[] doNotKeepValues) throws NullPointerException { 117 | Objects.requireNonNull(this.lastResponses); 118 | this.lastResponses.getBuilder().applyToWindow(this, doNotKeepValues); 119 | } 120 | 121 | /** 122 | * Get last player who responded this form. 123 | * 124 | * @return last player, nullable 125 | * 126 | * @see #setLastResponses(Player, FormResponseCustom) 127 | * @see #onEvent(FormWindow, FormResponse, Player) 128 | */ 129 | public Player getLastPlayer() { 130 | return lastPlayer; 131 | } 132 | 133 | public void setLastResponses(@NotNull Player player, @NotNull FormResponseCustom response) { 134 | Objects.requireNonNull(player); 135 | Objects.requireNonNull(response); 136 | 137 | this.lastPlayer = player; 138 | this.lastResponses = this.template.getBuilder().parseTemplateResponse(player, response, this.exceptionConsumer); 139 | } 140 | 141 | /** 142 | * 当玩家提交表单后, 输入了错误的格式时会抛出异常 143 | * 144 | * @param exceptionConsumer 处理异常的函数 145 | */ 146 | public final ResponsibleFormWindowTemplated setExceptionConsumer(@Nullable ExceptionConsumer exceptionConsumer) { 147 | this.exceptionConsumer = exceptionConsumer; 148 | return this; 149 | } 150 | 151 | /** 152 | * 在玩家提交表单后调用
153 | * Called on submitted
154 | * 即使表单某项目格式不正确, 也会调用此方法.
155 | * 请使用 {@link TemplateResponses#hasNull()} 判断
156 | * This method will still be called even if there are illegal input(s)
157 | * Please use {@link TemplateResponses#hasNull()} to check 158 | * 159 | * @param listener 调用的方法 160 | */ 161 | public final ResponsibleFormWindowTemplated onResponded(@NotNull BiConsumer, Player> listener) { 162 | this.buttonClickedListener = listener; 163 | return this; 164 | } 165 | 166 | /** 167 | * 在玩家提交表单后调用
168 | * Called on submitted
169 | * 即使表单某项目格式不正确, 也会调用此方法.
170 | * 请使用 {@link TemplateResponses#hasNull()} 判断
171 | * This method will still be called even if there are illegal input(s)
172 | * Please use {@link TemplateResponses#hasNull()} to check 173 | * 174 | * @param listener 调用的方法(无 Player) 175 | */ 176 | public final ResponsibleFormWindowTemplated onResponded(@NotNull Consumer> listener) { 177 | Objects.requireNonNull(listener); 178 | this.buttonClickedListener = (response, player) -> listener.accept(response); 179 | return this; 180 | } 181 | 182 | /** 183 | * 在玩家提交表单后调用
184 | * Called on submitted
185 | * 即使表单某项目格式不正确, 也会调用此方法.
186 | * 请使用 {@link TemplateResponses#hasNull()} 判断
187 | * This method will still be called even if there are illegal input(s)
188 | * Please use {@link TemplateResponses#hasNull()} to check 189 | * 190 | * @param listener 调用的方法(无参数) 191 | */ 192 | public final ResponsibleFormWindowTemplated onResponded(@NotNull Runnable listener) { 193 | Objects.requireNonNull(listener); 194 | this.buttonClickedListener = (id, player) -> listener.run(); 195 | return this; 196 | } 197 | 198 | /** 199 | * 在玩家关闭窗口而没有点击按钮提交表单后调用.
200 | * Called on closed without submitting. 201 | * 202 | * @param listener 调用的方法 203 | */ 204 | public final ResponsibleFormWindowTemplated onClosed(@NotNull Consumer listener) { 205 | this.windowClosedListener = listener; 206 | return this; 207 | } 208 | 209 | /** 210 | * 在玩家关闭窗口而没有点击按钮提交表单后调用.
211 | * Called on closed without submitting. 212 | * 213 | * @param listener 调用的方法 214 | */ 215 | public final ResponsibleFormWindowTemplated onClosed(@NotNull Runnable listener) { 216 | Objects.requireNonNull(listener); 217 | this.windowClosedListener = (player) -> listener.run(); 218 | return this; 219 | } 220 | 221 | /** 222 | * 不会重置 {@linkplain #lastResponses 最后的回复数据} 223 | * 224 | * @param response response 225 | * @param player player 226 | */ 227 | public void callClicked(@NotNull TemplateResponses response, @NotNull Player player) { 228 | Objects.requireNonNull(player); 229 | Objects.requireNonNull(response); 230 | 231 | this.onResponded(response, player); 232 | 233 | if (this.buttonClickedListener != null) { 234 | this.buttonClickedListener.accept(response, player); 235 | } 236 | } 237 | 238 | /** 239 | * 将表单重新发送给玩家, 并保留已经填写的格式正确的数据. 240 | */ 241 | public void resendWindow(@NotNull Player player) { 242 | this.resendWindow(player, null); 243 | } 244 | 245 | /** 246 | * 将表单重新发送给玩家, 并保留已经填写的格式正确的数据. 247 | * 248 | * @param doNotKeepValues keys that will not be filled 249 | */ 250 | public void resendWindow(@NotNull Player player, @Nullable K[] doNotKeepValues) { 251 | Objects.requireNonNull(player); 252 | if (this.lastResponses == null) { 253 | throw new UnsupportedOperationException(); 254 | } 255 | this.applyLastResponse(doNotKeepValues); 256 | player.showFormWindow(this); 257 | } 258 | 259 | 260 | public void callClosed(@NotNull Player player) { 261 | Objects.requireNonNull(player); 262 | 263 | this.onClosed(player); 264 | 265 | if (this.windowClosedListener != null) { 266 | this.windowClosedListener.accept(player); 267 | } 268 | } 269 | 270 | @Override 271 | public String getJSONData() { 272 | String toModify = new Gson().toJson(this, FormWindowCustom.class);//must!! 273 | return toModify.replace("defaultOptionIndex", "default") 274 | .replace("defaultText", "default") 275 | .replace("defaultValue", "default") 276 | .replace("defaultStepIndex", "default"); 277 | } 278 | 279 | 280 | @SuppressWarnings("unchecked") 281 | static boolean onEvent(FormWindow formWindow, FormResponse response, Player player) { 282 | if (formWindow instanceof ResponsibleFormWindowTemplated) { 283 | ResponsibleFormWindowTemplated window = (ResponsibleFormWindowTemplated) formWindow; 284 | 285 | if (window.wasClosed() || response == null) { 286 | window.callClosed(player); 287 | window.closed = false;//for resending 288 | } else { 289 | window.setLastResponses(player, (FormResponseCustom) response); 290 | window.callClicked(window.getLastResponses(), player); 291 | } 292 | return true; 293 | } 294 | return false; 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/WindowManager.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.event.EventHandler; 5 | import cn.nukkit.event.EventPriority; 6 | import cn.nukkit.event.Listener; 7 | import cn.nukkit.event.player.PlayerFormRespondedEvent; 8 | import cn.nukkit.event.player.PlayerKickEvent; 9 | import cn.nukkit.event.player.PlayerQuitEvent; 10 | import cn.nukkit.event.player.PlayerSettingsRespondedEvent; 11 | import cn.nukkit.form.response.FormResponse; 12 | import cn.nukkit.form.window.FormWindow; 13 | import moe.him188.gui.utils.Backable; 14 | 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | /** 19 | * 用于管理窗口回调 20 | * 21 | * @author Him188moe @ GUI Project 22 | */ 23 | public final class WindowManager { 24 | 25 | public static final class RespondedListener implements Listener { 26 | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) 27 | public void onResponded(PlayerFormRespondedEvent event) { 28 | processResponseEvent(event.getWindow(), event.getResponse(), event.getPlayer()); 29 | } 30 | 31 | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) 32 | public void onResponded(PlayerSettingsRespondedEvent event) { 33 | processResponseEvent(event.getWindow(), event.getResponse(), event.getPlayer()); 34 | } 35 | 36 | private void processResponseEvent(FormWindow window, FormResponse response, Player player) { 37 | if (ResponsibleFormWindowSimpleAdvanced.onEvent(window, response, player)) { 38 | return; 39 | } 40 | if (ResponsibleFormWindowSimple.onEvent(window, response, player)) { 41 | return; 42 | } 43 | if (ResponsibleFormWindowTemplated.onEvent(window, response, player)) { 44 | return; 45 | } 46 | if (ResponsibleFormWindowCustom.onEvent(window, response, player)) { 47 | return; 48 | } 49 | ResponsibleFormWindowModal.onEvent(window, response, player); 50 | } 51 | } 52 | 53 | public static final class GoBackListener implements Listener { 54 | private static Map lastWindows; 55 | 56 | public GoBackListener() { 57 | lastWindows = new HashMap<>(); 58 | } 59 | 60 | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) 61 | public void onResponded(PlayerFormRespondedEvent event) { 62 | FormWindow parent = lastWindows.remove(event.getPlayer().getLoaderId()); 63 | if (parent != null) { 64 | if (event.getWindow() instanceof Backable) { 65 | ((Backable) event.getWindow()).setParent(parent); 66 | } 67 | } 68 | lastWindows.put(event.getPlayer().getLoaderId(), event.getWindow()); 69 | } 70 | 71 | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) 72 | public void onQuit(PlayerQuitEvent event) { 73 | lastWindows.remove(event.getPlayer().getLoaderId()); 74 | } 75 | 76 | @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) 77 | public void onKick(PlayerKickEvent event) { 78 | lastWindows.remove(event.getPlayer().getLoaderId()); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/defaults/TipWindow.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window.defaults; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.form.element.ElementButton; 5 | import moe.him188.gui.window.ResponsibleFormWindowSimple; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Objects; 9 | import java.util.function.Consumer; 10 | 11 | /** 12 | * 提示窗口
13 | * Tip window 14 | * 15 | * @author Him188moe @ GUI Project 16 | * @deprecated Use {@link ResponsibleFormWindowSimple#ResponsibleFormWindowSimple(String, String, String...)} 17 | */ 18 | @Deprecated 19 | public class TipWindow extends ResponsibleFormWindowSimple { 20 | public TipWindow(String title, String content, String buttonText) { 21 | super(title, content, new ArrayList() { 22 | { 23 | add(new ElementButton(buttonText)); 24 | } 25 | }); 26 | } 27 | 28 | public TipWindow(String content, String buttonText) { 29 | this("提示 | Announcement", content, buttonText); 30 | } 31 | 32 | public TipWindow(String content) { 33 | this("提示 | Announcement", content, "OK"); 34 | } 35 | 36 | @Deprecated 37 | public TipWindow onClickedPlayerly(Consumer listener) { 38 | Objects.requireNonNull(listener); 39 | return (TipWindow) super.onClicked((id, player) -> listener.accept(player)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/listener/action/ClickListener.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window.listener.action; 2 | 3 | import cn.nukkit.Player; 4 | 5 | import java.util.function.Consumer; 6 | 7 | /** 8 | * @author Him188moe @ GUI Project 9 | */ 10 | @FunctionalInterface 11 | public interface ClickListener extends Consumer { 12 | void accept(Player player); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/listener/action/ClickListenerSimple.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window.listener.action; 2 | 3 | import cn.nukkit.Player; 4 | 5 | /** 6 | * @author Him188moe @ GUI Project 7 | */ 8 | @FunctionalInterface 9 | public interface ClickListenerSimple extends ClickListener { 10 | void run(); 11 | 12 | @Override 13 | default void accept(Player player) { 14 | run(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/listener/response/ResponseListener.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window.listener.response; 2 | 3 | /** 4 | * @author Him188moe @ GUI Project 5 | */ 6 | public interface ResponseListener { 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/listener/response/ResponseListenerAdvanced.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window.listener.response; 2 | 3 | import cn.nukkit.Player; 4 | 5 | /** 6 | * @author Him188moe @ GUI Project 7 | */ 8 | public interface ResponseListenerAdvanced extends ResponseListener { 9 | /** 10 | * 当表单提交数据时调用 11 | * 12 | * @param entry 数据 13 | * @param player player 14 | */ 15 | default void onClicked(E entry, Player player) { 16 | 17 | } 18 | 19 | /** 20 | * 当表单关闭而没有提交数据时调用 21 | * 22 | * @param player player 23 | */ 24 | default void onClosed(Player player) { 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/listener/response/ResponseListenerCustom.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window.listener.response; 2 | 3 | import cn.nukkit.Player; 4 | import cn.nukkit.form.response.FormResponseCustom; 5 | 6 | /** 7 | * @author Him188moe @ GUI Project 8 | */ 9 | public interface ResponseListenerCustom extends ResponseListener { 10 | /** 11 | * 当表单提交数据时调用 12 | * 13 | * @param response 数据 14 | * @param player player 15 | */ 16 | default void onClicked(FormResponseCustom response, Player player) { 17 | 18 | } 19 | 20 | /** 21 | * 当表单关闭而没有提交数据时调用 22 | * 23 | * @param player player 24 | */ 25 | default void onClosed(Player player) { 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/listener/response/ResponseListenerModal.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window.listener.response; 2 | 3 | import cn.nukkit.Player; 4 | 5 | /** 6 | * @author Him188moe @ GUI Project 7 | */ 8 | public interface ResponseListenerModal extends ResponseListener { 9 | /** 10 | * 当表单提交数据并关闭窗口时调用 11 | * 12 | * @param confirmation 点击按钮类型. true就是trueButton 13 | * @param player player 14 | */ 15 | default void onClicked(boolean confirmation, Player player) { 16 | 17 | } 18 | 19 | /** 20 | * 当表单关闭而没有提交数据时调用 21 | * 22 | * @param player player 23 | */ 24 | default void onClosed(Player player) { 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/listener/response/ResponseListenerSimple.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window.listener.response; 2 | 3 | import cn.nukkit.Player; 4 | 5 | /** 6 | * @author Him188moe @ GUI Project 7 | */ 8 | public interface ResponseListenerSimple extends ResponseListener { 9 | /** 10 | * 当表单提交数据并关闭窗口时调用.
11 | * Called when player clicked a button. 12 | * 13 | * @param id button id, starts from 0. | 按钮 ID, 从 0 开始 14 | * @param player player 15 | */ 16 | default void onClicked(int id, Player player) { 17 | 18 | } 19 | 20 | /** 21 | * 当表单关闭而没有提交数据时调用 22 | * 23 | * @param player player 24 | */ 25 | default void onClosed(Player player) { 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/moe/him188/gui/window/listener/response/ResponseListenerTemplate.java: -------------------------------------------------------------------------------- 1 | package moe.him188.gui.window.listener.response; 2 | 3 | import cn.nukkit.Player; 4 | import moe.him188.gui.template.response.TemplateResponses; 5 | 6 | /** 7 | * @author Him188moe @ GUI Project 8 | */ 9 | public interface ResponseListenerTemplate extends ResponseListener { 10 | /** 11 | * 当表单提交数据并关闭窗口时调用 12 | * 13 | * @param responses response 14 | * @param player player 15 | */ 16 | default void onResponded(TemplateResponses responses, Player player) { 17 | 18 | } 19 | 20 | /** 21 | * 当表单被关闭(点击右上角关闭按钮), 而没有提交数据时调用 22 | * 23 | * @param player player 24 | */ 25 | default void onClosed(Player player) { 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: GUI 2 | author: Him188moe 3 | main: moe.him188.gui.GUIPluginBase 4 | api: [1.0.0] 5 | version: 1.15.1 6 | description: "Form window tools" 7 | load: STARTUP -------------------------------------------------------------------------------- /src/test/java/TestTemplate.java: -------------------------------------------------------------------------------- 1 | import moe.him188.gui.template.Template; 2 | import moe.him188.gui.template.element.*; 3 | import moe.him188.gui.utils.InputTypes; 4 | 5 | import java.text.SimpleDateFormat; 6 | import java.util.Arrays; 7 | 8 | /** 9 | * @author Him188moe @ GUI Project 10 | */ 11 | public class TestTemplate extends Template { 12 | public enum ElementKey { 13 | INT_TEST, 14 | DATE_TEST, 15 | STRING_TEST, 16 | 17 | DROPDOWN, 18 | TOGGLE, 19 | SLIDER, 20 | STEP_SLIDER, 21 | } 22 | 23 | private static TestTemplate instance = new TestTemplate(); 24 | 25 | public static TestTemplate getInstance() { 26 | return instance; 27 | } 28 | 29 | public TestTemplate() { 30 | setTitle("Test Template"); 31 | 32 | addElement(new TemplateElementLabel<>("test label")); 33 | addElement(new TemplateElementInput<>(ElementKey.INT_TEST, "int test", InputTypes.INTEGER)); 34 | addElement(new TemplateElementInput<>(ElementKey.DATE_TEST, "date test", InputTypes.DATE(new SimpleDateFormat("yyyy/MM/dd HH:mm")))); 35 | addElement(new TemplateElementInput<>(ElementKey.STRING_TEST, "string test", InputTypes.STRING)); 36 | addElement(new TemplateElementDropdown<>(ElementKey.DROPDOWN, "dropdown", Arrays.asList("1", "2", "3"))); 37 | addElement(new TemplateElementToggle<>(ElementKey.TOGGLE, "toggle", true)); 38 | addElement(new TemplateElementSlider<>(ElementKey.SLIDER, "slider", 0, 100)); 39 | addElement(new TemplateElementStepSlider<>(ElementKey.STEP_SLIDER, "step slider", Arrays.asList("step1", "step2"))); 40 | } 41 | } -------------------------------------------------------------------------------- /src/test/java/TestWindow.java: -------------------------------------------------------------------------------- 1 | import cn.nukkit.Player; 2 | import moe.him188.gui.template.Template; 3 | import moe.him188.gui.template.element.TemplateElementInput; 4 | import moe.him188.gui.template.response.TemplateResponses; 5 | import moe.him188.gui.utils.ExceptionConsumerAll; 6 | import moe.him188.gui.utils.InputFormatException; 7 | import moe.him188.gui.utils.ResponseParseException; 8 | import moe.him188.gui.window.ResponsibleFormWindowTemplated; 9 | import moe.him188.gui.window.defaults.TipWindow; 10 | import moe.him188.gui.window.listener.response.ResponseListenerTemplate; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | /** 14 | * @author Him188moe @ GUI Project 15 | */ 16 | public class TestWindow extends ResponsibleFormWindowTemplated implements ResponseListenerTemplate { 17 | public TestWindow(Template template) { 18 | super(template); 19 | 20 | setExceptionConsumer(new ExceptionConsumerAll() { 21 | @Override 22 | public void accept(Player player, ResponseParseException[] exception) { 23 | StringBuilder message = new StringBuilder(); 24 | 25 | for (ResponseParseException parseException : exception) { 26 | if (message.length() != 0) { 27 | message.append("\n"); 28 | } 29 | 30 | if (parseException.getCause() instanceof InputFormatException) { 31 | TemplateElementInput input = (TemplateElementInput) parseException.getElement(); 32 | message.append("你输入的 ").append(input.getName()).append(" 有误. 请注意格式: \n ").append(input.getPlaceholder()); 33 | } 34 | } 35 | 36 | player.showFormWindow(new TipWindow(message.toString()).onClicked(() -> resendWindow(player))); 37 | } 38 | }); 39 | } 40 | 41 | @Override 42 | public void onResponded(@NotNull TemplateResponses responses, @NotNull Player player) { 43 | if (responses.hasNull()) { 44 | System.out.println("onResponded: null responses"); 45 | return; 46 | } 47 | 48 | System.out.println(responses.getInputInteger(TestTemplate.ElementKey.INT_TEST)); 49 | System.out.println(responses.getInputDate(TestTemplate.ElementKey.DATE_TEST)); 50 | System.out.println(responses.getInputString(TestTemplate.ElementKey.STRING_TEST)); 51 | System.out.println(responses.getDropdown(TestTemplate.ElementKey.DROPDOWN)); 52 | System.out.println(responses.getToggle(TestTemplate.ElementKey.TOGGLE)); 53 | System.out.println(responses.getSlider(TestTemplate.ElementKey.SLIDER)); 54 | 55 | player.showFormWindow(new TipWindow("Success")); 56 | } 57 | } --------------------------------------------------------------------------------