Skip to content
Merged
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
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
javaVersion=25
mcVersion=1.21.11
group=dev.slne.surf
version=1.21.11-2.64.0
version=1.21.11-2.65.0
relocationPrefix=dev.slne.surf.surfapi.libs
snapshot=false
99 changes: 76 additions & 23 deletions surf-api-bukkit/surf-api-bukkit-api/api/surf-api-bukkit-api.api

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,22 @@ import org.bukkit.plugin.java.JavaPlugin
* view.open(player)
* ```
*
* @param header the plain-text title string rendered in the inventory's title bar
* @param defaultHeader the plain-text title string rendered in the inventory's title bar
* @see surfView
* @see paginatedSurfView
* @see SurfViewSettings
*/
@Suppress("UnstableApiUsage")
abstract class AbstractSurfView(
private val header: String,
private val defaultHeader: String,
) : View() {
/**
* The [SurfViewSettings] controlling layout, cancel behaviours, font, and alignment.
* Defaults to [SimpleViewSettings] with all defaults applied.
*/
open val settings: SurfViewSettings = SimpleViewSettings()
private val container = ViewContainer()

private val containerState = lazyState { _ -> ViewContainer() }

/**
* Called during [onInit] after the container defaults are applied.
Expand Down Expand Up @@ -108,42 +109,41 @@ abstract class AbstractSurfView(
protected open fun onViewUpdate(update: Context) = Unit

/**
* Applies modifications to the [ViewContainer] and optionally updates the inventory title.
* Applies modifications to the [ViewContainer] and updates the inventory title.
*
* The [block] is executed within a [ViewContainerModificationContext] that provides
* component management functions. If [updateContext] is provided:
* component management functions. Title updates are propagated based on [context]:
* - For an [OpenContext], the title is set via `modifyConfig`.
* - For any other context, `updateTitleForEveryone` is called to update all viewers.
*
* @param updateContext optional context used to propagate the title change; `null` skips
* the title update (useful during initial setup)
* @param context context used to propagate the title change to viewers
* @param block modifications to apply to the [ViewContainer]
*/
protected fun modifyContainer(
updateContext: Context? = null,
context: Context,
block: context(ViewContainerModificationContext) () -> Unit
) {
val container = containerState.get(context)

context(ViewContainerModificationContext(container)) {
block()
}

if (updateContext != null) {
if (updateContext is OpenContext) {
updateContext.modifyConfig {
title(container.render())
}
} else {
updateContext.updateTitleForEveryone(container.render())
if (context is OpenContext) {
context.modifyConfig {
title(container.render())
}
} else {
context.updateTitleForEveryone(container.render())
}
}

private fun applyContainerDefaults() {
modifyContainer {
private fun applyContainerDefaults(context: Context) {
modifyContainer(context) {
addChild(ViewContainerGlyphComponent(settings.rows))
addChild(
ViewContainerTitleComponent(
title = header,
title = defaultHeader,
font = settings.font,
charSpacing = ViewContainerTitleComponent.CHAR_SPACING,
textAlignment = settings.headerTextAlignment
Expand All @@ -169,8 +169,6 @@ abstract class AbstractSurfView(
}

final override fun onInit(config: ViewConfigBuilder) {
applyContainerDefaults()

with(settings) {
if (cancelOnPickup) config.cancelOnPickup()
if (cancelOnDrag) config.cancelOnDrag()
Expand All @@ -180,12 +178,12 @@ abstract class AbstractSurfView(

onViewInit(config)

config.title(container.render())
config.size(settings.rows.rows)
config.type(ViewType.CHEST)
}

final override fun onOpen(open: OpenContext) {
applyContainerDefaults(open)
onViewOpen(open)
}

Expand Down Expand Up @@ -224,11 +222,13 @@ abstract class AbstractSurfView(
}

val player = click.player
val previousState = viewer.previousContext.initialData

viewer.previousContext = null
click.closeForPlayer()

player.scheduler.run(JavaPlugin.getProvidingPlugin(javaClass), {
viewFrame.open(previousView.javaClass, player)
viewFrame.open(previousView.javaClass, player, previousState)
}, null)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,26 +70,8 @@ abstract class PaginatedSurfViewDSLImpl @PublishedApi internal constructor(
ctx.containerDefaults?.invoke(modificationCtx, ref)
}

/**
* Modifies the [ViewContainer] of this paginated view from within a lifecycle callback.
*
* Only callable inside a [PaginatedSurfViewRef] context (i.e. within lifecycle callbacks).
* Delegates to the internal `modifyContainer` method of [AbstractSurfView].
*
* @param updateContext optional context used to propagate the updated title;
* pass `null` to skip the title update
* @param block modifications to apply to the [ViewContainer]
*/
context(_: PaginatedSurfViewRef)
fun modifyContainer(
updateContext: Context? = null,
block: context(ViewContainerModificationContext) () -> Unit
) {
modifyContainer0(updateContext, block)
}

private fun modifyContainer0(
updateContext: Context? = null,
internal fun modifyContainer0(
updateContext: Context,
block: context(ViewContainerModificationContext) () -> Unit
) = modifyContainer(updateContext, block)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import dev.slne.surf.surfapi.bukkit.api.inventory.framework.view.pagination.Abst
/**
* Creates a simple (non-paginated) [AbstractSurfView] using a DSL builder.
*
* The [block] is called with both a [SurfViewContext] (for lifecycle hooks and settings)
* and a [SurfViewRef] (for accessing the view inside callbacks) as context receivers.
* After the block executes, the concrete view implementation is instantiated and the
* reference is resolved.
* The [block] is called with a [SurfViewContext] as its context receiver, which exposes
* lifecycle hooks and view settings for configuration. After the block executes, the
* concrete view implementation is instantiated and its internal reference is resolved.
*
* ```kotlin
* val myView = surfView("Inventory Title") {
Expand All @@ -25,16 +24,16 @@ import dev.slne.surf.surfapi.bukkit.api.inventory.framework.view.pagination.Abst
* ```
*
* @param header the plain-text title rendered in the inventory header
* @param block DSL configuration block accepting both [SurfViewContext] and [SurfViewRef]
* @param block DSL configuration block with [SurfViewContext] as its context receiver
* @return the fully configured [AbstractSurfView]
* @see paginatedSurfView
* @see AbstractSurfView
*/
inline fun surfView(header: String, block: context (SurfViewContext, SurfViewRef) () -> Unit): AbstractSurfView {
inline fun surfView(header: String, block: context (SurfViewContext) () -> Unit): AbstractSurfView {
val ctx = SurfViewContext()
val ref = SurfViewRef()

context(ctx, ref) {
context(ctx) {
block()
}

Expand All @@ -47,9 +46,9 @@ inline fun surfView(header: String, block: context (SurfViewContext, SurfViewRef
/**
* Creates a paginated [AbstractPaginatedSurfView] using a DSL builder.
*
* Works the same as [surfView] but accepts a [PaginatedSurfViewContext] and
* [PaginatedSurfViewRef]. The [block] must configure at least a `layoutTarget` character
* and a `pagination { }` block, otherwise [IllegalStateException] is thrown at view creation.
* Works similarly to [surfView] but uses a [PaginatedSurfViewContext] as the context
* receiver. The [block] must configure at least a `layoutTarget` character and a
* `pagination { }` block, otherwise [IllegalStateException] is thrown at view creation.
*
* ```kotlin
* val listView = paginatedSurfView("Item List") {
Expand All @@ -68,19 +67,19 @@ inline fun surfView(header: String, block: context (SurfViewContext, SurfViewRef
* ```
*
* @param header the plain-text title rendered in the inventory header
* @param block DSL configuration block accepting both [PaginatedSurfViewContext] and [PaginatedSurfViewRef]
* @param block DSL configuration block with [PaginatedSurfViewContext] as its context receiver
* @return the fully configured [AbstractPaginatedSurfView]
* @see surfView
* @see dev.slne.surf.surfapi.bukkit.api.inventory.framework.view.pagination.AbstractPaginatedSurfView
*/
inline fun paginatedSurfView(
header: String,
block: context (PaginatedSurfViewContext, PaginatedSurfViewRef) () -> Unit
block: context (PaginatedSurfViewContext) () -> Unit
): AbstractPaginatedSurfView {
val ctx = PaginatedSurfViewContext()
val ref = PaginatedSurfViewRef()

context(ctx, ref) {
context(ctx) {
block()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,37 +66,8 @@ abstract class SurfViewDSLImpl @PublishedApi internal constructor(
ctx.containerDefaults?.invoke(modificationCtx, ref)
}

/**
* Modifies the [ViewContainer] of this view from within a lifecycle callback.
*
* This function is only callable inside a [SurfViewRef] context (i.e. within lifecycle
* callbacks). It is a type-safe forwarding wrapper that delegates to the internal
* `modifyContainer` method of [AbstractSurfView].
*
* ```kotlin
* onFirstRender {
* with(view) {
* modifyContainer {
* blockRow(5)
* }
* }
* }
* ```
*
* @param updateContext optional context used to propagate the updated title;
* pass `null` to skip the title update
* @param block modifications to apply to the [ViewContainer]
*/
context(_: SurfViewRef)
fun modifyContainer(
updateContext: Context? = null,
block: context(ViewContainerModificationContext) () -> Unit
) {
modifyContainer0(updateContext, block)
}

private fun modifyContainer0(
updateContext: Context? = null,
internal fun modifyContainer0(
updateContext: Context,
block: context(ViewContainerModificationContext) () -> Unit
) = modifyContainer(updateContext, block)
}
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,24 @@ fun <ViewRef : AbstractSurfViewRef> containerDefaults(block: context (@Inventory
ctx.containerDefaults = block
}


/**
* Modifies the [ViewContainer] of this view from within a lifecycle callback.
* This function is only callable inside a [AbstractSurfViewRef] context (i.e. within lifecycle
* callbacks). It is a type-safe forwarding wrapper that delegates to the internal `modifyContainer` method of [AbstractSurfView].
*
*/
context(ref: AbstractSurfViewRef)
fun Context.modifyContainer(
block: context(ViewContainerModificationContext) () -> Unit
) {
when (val view = ref.getRegisteredView()) {
is SurfViewDSLImpl -> view.modifyContainer0(this, block)
is PaginatedSurfViewDSLImpl -> view.modifyContainer0(this, block)
else -> error("Unknown view type: ${view::class}")
}
}

/**
* Configures the [SimpleViewSettings] for this simple (non-paginated) view.
*
Expand Down Expand Up @@ -225,4 +243,4 @@ fun settings(block: @InventoryFrameworkDSL PaginatedViewSettingsBuilder.() -> Un
context(ctx: PaginatedSurfViewContext)
fun layoutTarget(target: Char) {
ctx.layoutTarget = target
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import dev.slne.surf.surfapi.core.api.messages.builder.SurfComponentBuilder
* texture width 15 pixels) to hint to the player that clicking outside the inventory navigates back.
*/
data object ViewContainerBackHintComponent : ViewContainerComponent {
override val positionalShift = -21
override val textureWidth = 15
override val positionalShift = -22
override val textureWidth = 15 + 2 // 2 pixel for spacing

override fun SurfComponentBuilder.renderComponent() {
text("ꐷ")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import net.kyori.adventure.key.Key
* @param charSpacing the pixel spacing to insert between each character
* @param textAlignment the [TextAlignment] controlling horizontal positioning
*/
internal class ViewContainerTitleComponent(
class ViewContainerTitleComponent(
title: String,
private val font: Key,
charSpacing: Int,
Expand Down Expand Up @@ -61,8 +61,7 @@ internal class ViewContainerTitleComponent(
)

override val positionalShift = textAlignment.calculateShift(title, alignmentOptions)
override val textureWidth =
TextAlignment.calculateTextWidth(title, alignmentOptions) - 1 // -1 because of the last char spacing
override val textureWidth = TextAlignment.calculateTextWidth(title, alignmentOptions)

override fun SurfComponentBuilder.renderComponent() {
text(formattedTitle)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package dev.slne.surf.surfapi.bukkit.api.inventory.framework.view.container.dsl

import dev.slne.surf.surfapi.bukkit.api.inventory.framework.view.AbstractSurfViewRef
import dev.slne.surf.surfapi.bukkit.api.inventory.framework.view.container.component.ViewContainerComponent
import dev.slne.surf.surfapi.bukkit.api.inventory.framework.view.container.component.components.ViewBlockCellComponent
import dev.slne.surf.surfapi.bukkit.api.inventory.framework.view.container.component.components.ViewContainerBackHintComponent
import dev.slne.surf.surfapi.bukkit.api.inventory.framework.view.container.component.components.ViewContainerTitleComponent
import it.unimi.dsi.fastutil.ints.IntCollection
import it.unimi.dsi.fastutil.ints.IntLists

Expand Down Expand Up @@ -164,4 +166,28 @@ fun blockColumn(column: Int, exemptRows: IntCollection = IntLists.EMPTY_LIST) {
context(context: ViewContainerModificationContext)
fun backHint() {
addChild(ViewContainerBackHintComponent)
}

/**
* Sets a title header for the [ViewContainer][dev.slne.surf.surfapi.bukkit.api.inventory.framework.view.container.ViewContainer] by removing any existing title component
* and adding a new `ViewContainerTitleComponent` with the specified header text.
*
* @param header The text to set as the title for the container.
* @param context Provides access to the container modification API for the current view.
* @param ref A deferred reference to the associated view, providing configuration details
* such as font and text alignment settings.
*/
context(context: ViewContainerModificationContext, ref: AbstractSurfViewRef)
fun header(header: String) {
removeChildrenOfType<ViewContainerTitleComponent>()

val settings = ref.getRegisteredView().settings
addChild(
ViewContainerTitleComponent(
header,
settings.font,
ViewContainerTitleComponent.CHAR_SPACING,
settings.headerTextAlignment
)
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package dev.slne.surf.surfapi.bukkit.api.inventory.framework.view.icon

import dev.slne.surf.surfapi.bukkit.api.builder.buildItem
import dev.slne.surf.surfapi.core.api.messages.adventure.key
import io.papermc.paper.datacomponent.DataComponentTypes
import org.bukkit.inventory.ItemStack
import org.bukkit.inventory.ItemType

fun viewIcon(
icon: ViewIconType,
color: ViewIconColor,
init: ItemStack.() -> Unit = {}
) = ViewIcon(icon, color).build(init)

class ViewIcon(
val icon: ViewIconType,
val color: ViewIconColor,
) {
val itemModel: String
get() = "surf_menu_icon_${color.configName}_${icon.configName}"

@Suppress("UnstableApiUsage")
fun build(init: ItemStack.() -> Unit) = buildItem(ItemType.PAPER) {
init()

setData(DataComponentTypes.ITEM_MODEL, key("nexo", itemModel))
}
}
Loading