CabinGame/Luxe/blocks/ui/scroll_box.wren

177 lines
No EOL
6.2 KiB
Text

import "luxe: ui/control" for Control
import "luxe: ui" for UIContain, UIBehave
import "luxe: world" for UI, World, UIEvent
import "luxe: draw" for PathStyle
import "luxe: assets" for Assets
import "luxe: render" for Image
import "luxe: math" for Math
import "luxe: game" for Frame
import "globals" for Globals
import "math/rect" for AABB
import "math/math" for M
import "blocks/debug" for DrawDebug
import "blocks/ui/slider" for UiSlider
import "math/util" for Util
class UiScrollBox{
static create(ent){
var box = Control.create(ent)
var style = PathStyle.new()
style.color = [1,1,1,1]
style.thickness = 1
Control.set_allow_input(box, true)
var state = {"style": style, "pressed": false, "position": 0, "scrollSpeed": 4, "ent": ent, "spring": 32, "contentHeight": 0, "drag_cancelled":true}
Control.set_state_data(box, state)
var slider = UiSlider.create(ent)
Control.child_add(box, slider)
UiSlider.set_line(slider, false)
UiSlider.set_direction(slider, UiSlider.vertical)
UiSlider.set_handle(slider, Assets.image("assets/wip/Knob3"))
UiSlider.set_value(slider, 0)
Control.set_size(slider, 7, -1)
Control.set_behave(slider, UIBehave.right | UIBehave.vfill) //|
Control.set_margin(slider, 0, 0, 0, 0)
Control.set_events(slider){|event|
if(event.type == UIEvent.change){
var position = 0
var max_height = (Control.get_height(state["childContainer"]) + 1) - state["contentHeight"]
if(max_height < 0){
position = M.lerp(0, max_height, event.change)
}
set_position(box, position)
}
}
state["slider"] = slider
var childContainer = Control.create(ent)
Control.child_add(box, childContainer)
Control.set_behave(childContainer, UIBehave.fill)
Control.set_margin(childContainer, 1, 1, 7, 2) //don't include border, fix bottom bug, freedom to side slider
Control.set_clip(childContainer, true)
Control.set_contain(childContainer, UIContain.column | UIContain.start) //|
Control.set_allow_input(childContainer, true)
Control.set_events(childContainer){ |event|
if(!Util.valid_event(event, state["drag_cancelled"])) return
if(event.type == UIEvent.press) {
Control.set_state_data(childContainer, true)
UI.capture(childContainer)
}
if(event.type == UIEvent.release) {
Control.set_state_data(childContainer, false)
UI.uncapture(childContainer)
}
if(event.type == UIEvent.move && Control.get_state_data(childContainer)){
var state = Control.get_state_data(box)
var scrollPos = state["position"]
scrollPos = scrollPos + event.y_rel / 5
set_position(box, scrollPos)
}
}
state["childContainer"] = childContainer
var alignmentChild = Control.create(ent)
Control.child_add(childContainer, alignmentChild)
Control.set_size(alignmentChild, -1, 0)
Control.set_behave(alignmentChild, UIBehave.hfill | UIBehave.top) //| //make it wide
Control.set_margin(alignmentChild, 0, state["position"], 0, 0) //pos will always be 0 here, but writing it explicitly makes intent clearer
state["alignmentChild"] = alignmentChild
Control.set_render(box) {|control, state, x, y, w, h|
var scrollPos = state["position"]
if(scrollPos >= 0.5){
scrollPos = M.lerp(scrollPos, 0, M.pow2(-state["spring"] * Globals["Delta"]))
set_position(control, scrollPos, true)
}
var container_height = Control.get_height(childContainer) + 1 //+1 is here to fix clip bug
var max_pos = Math.min(container_height - state["contentHeight"], 0)
if(scrollPos <= max_pos - 0.5){
scrollPos = M.lerp(scrollPos, max_pos, M.pow2(-state["spring"] * Globals["Delta"]))
set_position(control, scrollPos, true)
}
var depth = UI.draw_depth_of(control, 0)
UI.draw_rect(control, x+0.5, y+0.5, depth, w-1, h-1, 0, state["style"])
}
Control.set_process(box){|control, state, event, x, y, w, h|
if(event.control != control) return
if(event.type == UIEvent.scroll){
var scrollPos = state["position"]
scrollPos = scrollPos + event.y * state["scrollSpeed"] * -1
set_position(control, scrollPos)
}
}
return box
}
static add(box, child){
var state = Control.get_state_data(box)
Control.child_add(state["childContainer"], child)
var ent = state["ent"]
UI.commit(ent)
update_content(box)
}
static remove(box, child){
var state = Control.get_state_data(box)
Control.child_remove(state["childContainer"], child)
var ent = state["ent"]
UI.commit(ent)
update_content(box)
}
static update_content(box){
var state = Control.get_state_data(box)
var container = state["childContainer"]
var child_count = Control.child_count(container)
//if only the alignmentChild exists
if(child_count <= 1){
state["contentHeight"] = 0
return
}
var first_child = Control.child_get(container, 1)
var upper_bound = Control.get_pos_y_abs(first_child)
var last_child = Control.child_get(container, child_count - 1)
var lower_bound = Control.get_pos_y_abs(last_child) + Control.get_height(last_child)
state["contentHeight"] = lower_bound - upper_bound //its lower - upper because of the y-negative coordinates
}
static set_position(box, position) {
set_position(box, position, false)
}
static set_drag_cancelled(box, drag_cancelled){
var state = Control.get_state_data(box)
state["drag_cancelled"] = drag_cancelled
}
static set_position(box, position, delay_commit){
var state = Control.get_state_data(box)
state["position"] = position
var ent = state["ent"]
Control.set_margin(state["alignmentChild"], 0, position, 0, 0)
if(!delay_commit){
} else {
Frame.end{
}
}
var rel_pos = 0
var max_height = (Control.get_height(state["childContainer"]) + 1) - state["contentHeight"]
if(max_height < 0){
rel_pos = M.clamp(M.inv_lerp(0, max_height, position), 0, 1)
}
UiSlider.set_value(state["slider"], rel_pos)
}
}