SectrPrototype/code/ui_signal.odin
Ed_ 1b32fe916e Large refactor of the entire codebase
Saw that layout really should be separated from the style struct, so went ahead and pulled the trigger...
A bunch of other refactors have also been done

* Lifted layout out of style, its not separate in UI_Box and in UI_State there is not a UI_LayoutCombo stack.
* UI_StyleTheme renamed to UI_StyleCombo
* UI_Theme has both UI_StyleCombo & UI_LayoutCombo
* Made files for the "project" related code
* ui_layout_compute moved to its own file, ui_layout now used for layout related data structures and interfacing
* Impovements to horizontal & vertical box impl
* UI_Box now keeps track of how many ancestors it has
2024-05-11 22:38:05 -04:00

255 lines
5.6 KiB
Odin

package sectr
import "base:runtime"
UI_Signal :: struct {
cursor_pos : Vec2,
drag_delta : Vec2,
scroll : Vec2,
left_clicked : b8,
right_clicked : b8,
double_clicked : b8,
keyboard_clicked : b8,
active : b8,
was_active : b8,
pressed : b8,
released : b8,
cursor_over : b8,
commit : b8,
}
ui_signal_from_box :: proc ( box : ^ UI_Box, update_style := true, update_deltas := true ) -> UI_Signal
{
// profile(#procedure)
ui := get_state().ui_context
input := get_state().input
frame_delta := frametime_delta32()
signal := UI_Signal {}
// Cursor Collision
// profile_begin( "Cursor collision")
signal.cursor_pos = ui_cursor_pos()
signal.cursor_over = cast(b8) pos_within_range2( signal.cursor_pos, box.computed.bounds )
// TODO(Ed): We eventually need to setup a sorted root based on last active history
UnderCheck:
{
if ! signal.cursor_over do break UnderCheck
last_root := ui_box_from_key( ui.prev_cache, ui.root.key )
if last_root == nil do break UnderCheck
top_ancestor := ui_top_ancestor(box)
if top_ancestor.parent_index != last_root.num_children - 1
{
for curr := last_root.last; curr != nil && curr.key != box.key; curr = curr.prev {
if pos_within_range2( signal.cursor_pos, curr.computed.bounds ) && curr.parent_index > top_ancestor.parent_index {
signal.cursor_over = false
}
}
}
}
// profile_end()
// profile_begin("misc")
left_pressed := pressed( input.mouse.left )
left_released := released( input.mouse.left )
mouse_clickable := UI_BoxFlag.Mouse_Clickable in box.flags
keyboard_clickable := UI_BoxFlag.Keyboard_Clickable in box.flags
was_hot := (box.hot_delta > 0)
was_active := (ui.active == box.key) && (box.active_delta > 0)
was_disabled := box.disabled_delta > 0
if was_hot {
// runtime.debug_trap()
}
if mouse_clickable && signal.cursor_over && left_pressed && was_hot
{
//TODO(Ed): We need to add the reorder of top-level widgets based on this interaction
// runtime.debug_trap()
// ui.hot = box.key
ui.active = box.key
ui.active_mouse[MouseBtn.Left] = box.key
ui.last_pressed_key = box.key
ui.active_start_style = box.style
signal.pressed = true
// TODO(Ed) : Support double-click detection
}
if mouse_clickable && ! signal.cursor_over && left_released
{
box.active_delta = 0
ui.active = UI_Key(0)
ui.active_mouse[MouseBtn.Left] = UI_Key(0)
signal.released = true
signal.left_clicked = false
}
if keyboard_clickable
{
// TODO(Ed) : Add keyboard interaction support
}
// TODO(Ed) : Add scrolling support
if UI_BoxFlag.Scroll_X in box.flags {
}
if UI_BoxFlag.Scroll_Y in box.flags {
}
// TODO(Ed) : Add panning support
if UI_BoxFlag.Pan_X in box.flags {
}
if UI_BoxFlag.Pan_Y in box.flags {
}
is_disabled := UI_BoxFlag.Disabled in box.flags
is_hot := ui.hot == box.key
is_active := ui.active == box.key
if mouse_clickable && signal.cursor_over && ! is_disabled
{
hot_vacant := ui.hot == UI_Key(0)
active_vacant := ui.active == UI_Key(0)
// (active_vacant is_active)
if signal.cursor_over
{
if ! hot_vacant {
prev := ui_box_from_key( ui.curr_cache, ui.hot )
prev.hot_delta = 0
}
// prev_hot := zpl_hmap_get( ui.prev_cache, u64(ui.hot) )
// prev_hot_label := prev_hot != nil ? prev_hot.label.str : ""
// log( str_fmt_tmp("Detected HOT via CURSOR OVER: %v is_hot: %v is_active: %v prev_hot: %v", box.label.str, is_hot, is_active, prev_hot_label ))
ui.hot = box.key
is_hot = true
ui.hot_start_style = box.style
}
}
else if ! signal.cursor_over && was_hot
{
ui.hot = UI_Key(0)
is_hot = false
box.hot_delta = 0
}
if mouse_clickable && signal.cursor_over && left_released
{
box.active_delta = 0
ui.active = UI_Key(0)
ui.active_mouse[MouseBtn.Left] = UI_Key(0)
signal.released = true
if was_active {
signal.left_clicked = true
ui.last_clicked = box.key
}
}
// profile_end()
// State Deltas update
// profile_begin( "state deltas upate")
if is_hot
{
box.hot_delta += frame_delta
if was_hot {
box.style_delta += frame_delta
}
}
if is_active
{
box.active_delta += frame_delta
if was_active {
box.style_delta += frame_delta
}
}
else {
box.active_delta = 0
}
if is_disabled
{
box.disabled_delta += frame_delta
if was_hot {
box.style_delta += frame_delta
}
}
else {
box.disabled_delta = 0
}
// profile_end()
signal.active = cast(b8) is_active
signal.was_active = cast(b8) was_active
// logf("was_active: %v", was_active)
// Update style if not in default state
if update_style
{
// profile("Update style")
if is_hot
{
if ! was_hot {
box.prev_style = box.style
box.style_delta = 0
}
box.layout = ui_layout_peek().hot
box.style = ui_style_peek().hot
}
if is_active
{
if ! was_active {
box.prev_style = box.style
box.style_delta = 0
}
box.layout = ui_layout_peek().active
box.style = ui_style_peek().active
}
if is_disabled
{
if ! was_disabled {
box.prev_style = box.style
box.style_delta = 0
}
box.layout = ui_layout_peek().disabled
box.style = ui_style_peek().disabled
}
if ! is_disabled && ! is_active && ! is_hot {
if was_disabled || was_active || was_hot {
box.prev_style = box.style
box.style_delta = 0
}
else {
box.style_delta += frame_delta
}
box.layout = ui_layout_peek().default
box.style = ui_style_peek().default
}
}
if is_active && ! was_active {
ui.active_start_signal = signal
}
return signal
}