What it does
cursor
Editor cursor management — resolves cursor variants from input and
picking state, normalises spec tables into the canonical shape the
engine cursor namespace accepts, and forwards them via cursor.set
/ cursor.clear. Used by the editor's hover and click feedback to
switch between visual cursor variants without per-call boilerplate.
Variant resolution priority (highest first):
hover+button → button → hover → default.
Exports
M.normalizeSpec(spec: CursorSpec?) -> CursorSpec?— canonicalise an image or model cursor spec. Returnsnilfor malformed specs.M.sampleState(overrides: SampleOverrides?) -> CursorState— snapshot input + picking state for variant resolution. Passoverridesto forcehit/button/hovered.M.resolveVariant(variants: CursorVariants, state: CursorState?) -> (CursorSpec?, string?)— pick the winning variant forstate(or a freshsampleState()if omitted). Returns the normalised spec plus the key that won.M.set(spec: CursorSpec) -> boolean— normalise then install viacursor.set. Returnsfalsefor invalid specs or a missing engine namespace.M.clear() -> boolean— clear the active custom cursor.M.applyVariant(variants: CursorVariants, state: CursorState?) -> (CursorSpec?, string?)— resolve + install in one call, or clear when nothing matched.
Types:
CursorSpec = { kind: string, visible?, hideNative?, image?, model? }.CursorImage = { src, size?, hotspot?, scale?, rotation? }.CursorModel = { url, size?, hotspot?, scale?, rotationQuat?, offset?, billboard? }.CursorVariants = { default?, hover?, button?, "hover+button"?, hoverButton? }.SampleOverrides = { hit?, button?, hovered? }.CursorState = { hit, hovered, hoveredEntityId, button, buttonDown, pointerLocked }.
Usage
local Cursor = require("@builtin::modules.engine.editor.cursor")
Cursor.applyVariant({
default = { kind = "image", image = { src = "/cursor.svg" } },
hover = { kind = "image", image = { src = "/cursor_hover.svg" } },
button = { kind = "image", image = { src = "/cursor_press.svg" } },
})
Notes
normalizeSpecis the only validation entry point — every other public function routes through it. A spec that fails to normalise is silently ignored.sampleStatereads fromZin.state.*andPicking.*each call; it is fast enough to call every frame but does not memoise.set/clearreturnfalsewhen the globalcursornamespace is missing (engine running with cursor module disabled). Callers can treat that as "no cursor support in this build" and skip.applyVariantcallsclearwhen no variant matches, so wiring an emptyvariantstable is a valid way to reset the cursor.
Discussion
Scoped to this part · feeds back into the world's score.
Sign in to post.sign in
No comments yet. Be the first.