Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions surf-api-paper/surf-api-paper-plugin-test/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ description = "surf-api-paper-plugin-test"

dependencies {
compileOnlyApi(projects.surfApiPaper.surfApiPaper)
compileOnlyApi(projects.surfApiPaper.surfApiPaperServer)
compileOnlyApi(libs.commandapi.paper)

paperweight.paperDevBundle(libs.paper.api.get().version)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import dev.slne.surf.api.paper.test.command.subcommands.SuspendCommandExecutionTest;
import dev.slne.surf.api.paper.test.command.subcommands.ToastTest;
import dev.slne.surf.api.paper.test.command.subcommands.VisualizerTest;
import dev.slne.surf.surfapi.bukkit.test.command.subcommands.display.DisplayTest;

public class SurfApiTestCommand extends CommandAPICommand {

Expand All @@ -39,7 +40,8 @@ public SurfApiTestCommand() {
new InventoryTest("inventory"),
new ToastTest("toast"),
new SuspendCommandExecutionTest("suspendCommandExecution"),
new SummonCommandTest("summoncommand")
new SummonCommandTest("summoncommand"),
new DisplayTest("display")
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
package dev.slne.surf.surfapi.bukkit.test.command.subcommands.display

import dev.jorel.commandapi.CommandAPICommand
import dev.jorel.commandapi.kotlindsl.playerExecutor
import dev.jorel.commandapi.kotlindsl.subcommand
import dev.slne.surf.api.paper.display.color
import dev.slne.surf.api.paper.display.cursor.CursorStyle
import dev.slne.surf.api.paper.display.document.document
import dev.slne.surf.api.paper.display.shape.Shape
import dev.slne.surf.api.paper.display.style.*
import dev.slne.surf.api.paper.server.display.Display
import dev.slne.surf.api.paper.server.display.DisplayManager
import dev.slne.surf.api.paper.server.display.modal.confirmDialog

/**
* Test command for the Display/UI API framework.
*
* Usage:
* - `/surfapitest display open` — Opens a demo display with styled UI elements
* - `/surfapitest display close` — Closes the active display
*/
class DisplayTest(name: String) : CommandAPICommand(name) {

// Catppuccin Mocha palette
companion object {
private val BG = color(0x1E1E2E)
private val SURFACE = color(0x313244)
private val OVERLAY = color(0x45475A)
private val TEXT = color(0xCDD6F4)
private val SUBTEXT = color(0x9399B2)
private val BLUE = color(0x89B4FA)
private val GREEN = color(0xA6E3A1)
private val RED = color(0xF38BA8)
private val YELLOW = color(0xF9E2AF)
private val MAUVE = color(0xCBA6F7)
private val BORDER = color(0x585B70)
}

init {
subcommand("open") {
playerExecutor { player, _ ->
// Create a 384x256 pixel document (3x2 map tiles)
val doc = document(384, 256) {
style {
backgroundColor = BG
padding = Insets.all(8)
gap = 6
}

// --- Title Bar ---
div {
style {
backgroundColor = SURFACE
padding = Insets(6, 10, 6, 10)
flexDirection = FlexDirection.ROW
alignItems = AlignItems.CENTER
gap = 8
}
shape(Shape.circle(4, filled = true)) {
style { color = BLUE }
}
label("Surf Display API Demo") {
style { color = TEXT; fontSize = 16 }
}
}

// --- Content Area ---
div {
style {
flexDirection = FlexDirection.ROW
gap = 8
}

// Left column - Shape showcase
div {
style {
width = 120
backgroundColor = SURFACE
padding = Insets.all(6)
gap = 4
border = Border(1, BORDER)
}
label("Shapes") {
style { color = BLUE; fontSize = 12 }
}
div {
style {
flexDirection = FlexDirection.ROW
gap = 6
alignItems = AlignItems.CENTER
}
shape(Shape.circle(8, filled = true)) {
style { color = GREEN }
}
shape(Shape.rectangle(16, 16, filled = true)) {
style { color = RED }
}
shape(Shape.triangle(16, 14, filled = true)) {
style { color = YELLOW }
}
}
div {
style {
flexDirection = FlexDirection.ROW
gap = 6
alignItems = AlignItems.CENTER
}
shape(Shape.ellipse(10, 6, filled = true)) {
style { color = MAUVE }
}
shape(Shape.roundedRectangle(20, 14, 4, filled = true)) {
style { color = BLUE }
}
}
}

// Right column - Interactive elements
div {
style {
backgroundColor = SURFACE
padding = Insets.all(6)
gap = 4
border = Border(1, BORDER)
}
label("Interactive Elements") {
style { color = BLUE; fontSize = 12 }
}

// Clickable button
div {
style {
backgroundColor = OVERLAY
padding = Insets(3, 8, 3, 8)
border = Border(1, GREEN)
cursor = CursorStyle.POINTER
}
label("Click Me!") {
style { color = GREEN; fontSize = 11 }
}
hoverable(
onEnter = { _ ->
style.backgroundColor = color(0x585B70)
},
onExit = { _ ->
style.backgroundColor = OVERLAY
}
)
onClick { ctx ->
player.sendMessage("Display clicked at (${ctx.pixelX}, ${ctx.pixelY})!")
}
}

// Another button that opens a modal
div {
style {
backgroundColor = OVERLAY
padding = Insets(3, 8, 3, 8)
border = Border(1, MAUVE)
cursor = CursorStyle.POINTER
}
label("Show Modal") {
style { color = MAUVE; fontSize = 11 }
}
hoverable(
onEnter = { _ ->
style.backgroundColor = color(0x585B70)
},
onExit = { _ ->
style.backgroundColor = OVERLAY
}
)
}

label("Text alignment:") {
style { color = SUBTEXT; fontSize = 10 }
}

// Text alignment demo
div {
style {
backgroundColor = color(0x11111B)
padding = Insets.all(3)
gap = 1
}
label("Left aligned") {
style { color = TEXT; fontSize = 10; textAlign = TextAlign.LEFT }
}
label("Center aligned") {
style { color = TEXT; fontSize = 10; textAlign = TextAlign.CENTER }
}
label("Right aligned") {
style { color = TEXT; fontSize = 10; textAlign = TextAlign.RIGHT }
}
}
}
}

// --- Footer ---
div {
style {
backgroundColor = SURFACE
padding = Insets(4, 10, 4, 10)
flexDirection = FlexDirection.ROW
justifyContent = JustifyContent.SPACE_BETWEEN
}
label("Sneak to close") {
style { color = SUBTEXT; fontSize = 10 }
}
label("Surf API v1.0") {
style { color = SUBTEXT; fontSize = 10 }
}
}
}

val display = Display(doc)

// Wire up the "Show Modal" button to actually show a modal
// The second child of the content area's right column (index 1) is the modal button
val contentArea = doc.root.children[1] // content row
val rightColumn = contentArea.children[1] // right column div
val modalButton = rightColumn.children[1] // "Show Modal" button
modalButton.onClick { _ ->
val modal = display.confirmDialog(
title = "Confirm Action",
message = "This is a modal dialog demo.\nDo you want to proceed?",
onConfirm = {
player.sendMessage("Confirmed!")
display.dismissModal()
},
onCancel = {
player.sendMessage("Cancelled!")
display.dismissModal()
}
)
display.showModal(modal)
}

DisplayManager.open(player, display)
player.sendMessage("Display opened! Look around to move cursor. Sneak to close.")
}
}

subcommand("close") {
playerExecutor { player, _ ->
if (DisplayManager.hasDisplay(player.uniqueId)) {
DisplayManager.close(player)
player.sendMessage("Display closed.")
} else {
player.sendMessage("No active display.")
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dev.slne.surf.api.paper.server

import dev.slne.surf.api.core.server.CoreInstance
import dev.slne.surf.api.paper.SurfApiPaper
import dev.slne.surf.api.paper.server.display.DisplayLoader
import dev.slne.surf.api.paper.server.impl.SurfApiPaperImpl
import dev.slne.surf.api.paper.server.inventory.framework.InventoryLoader
import dev.slne.surf.api.paper.server.listener.ListenerManager
Expand All @@ -22,6 +23,7 @@ object PaperInstance : CoreInstance() {
super.onEnable()

PacketApiLoader.onEnable()
DisplayLoader.onEnable()
InventoryLoader.enable()
ListenerManager.registerListeners()
(SurfApiPaper.INSTANCE as SurfApiPaperImpl).onEnable()
Expand All @@ -30,6 +32,7 @@ object PaperInstance : CoreInstance() {
override suspend fun onDisable() {
super.onDisable()

DisplayLoader.onDisable()
ListenerManager.unregisterListeners()
PacketApiLoader.onDisable()
InventoryLoader.disable()
Expand Down
Loading