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 ) -> 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 { // 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 { // profile("Update style") if is_hot { if ! was_hot { box.prev_style = box.style box.style_delta = 0 } box.style = stack_peek( & ui.theme_stack ).hot } if is_active { if ! was_active { box.prev_style = box.style box.style_delta = 0 } box.style = stack_peek( & ui.theme_stack ).active } if is_disabled { if ! was_disabled { box.prev_style = box.style box.style_delta = 0 } box.style = stack_peek( & ui.theme_stack ).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.style = stack_peek( & ui.theme_stack ).default } } if is_active && ! was_active { ui.active_start_signal = signal } return signal }