2018-07-14 01:13:05 +00:00
|
|
|
/*
|
|
|
|
* Helpers for ui data structures.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// TOP
|
|
|
|
|
2018-12-17 02:07:49 +00:00
|
|
|
// TODO(allen): documentation comment here
|
2018-10-01 19:32:28 +00:00
|
|
|
|
2019-02-26 19:59:57 +00:00
|
|
|
////////////////////////////////
|
|
|
|
|
|
|
|
typedef u32 View_Get_UI_Flags;
|
|
|
|
enum{
|
|
|
|
ViewGetUIFlag_KeepDataAsIs = 0,
|
|
|
|
ViewGetUIFlag_ClearData = 1,
|
|
|
|
};
|
|
|
|
|
|
|
|
static b32
|
|
|
|
view_get_ui_data(Application_Links *app, View_ID view_id, View_Get_UI_Flags flags, UI_Data **ui_data_out, Arena **ui_arena_out){
|
|
|
|
b32 result = false;
|
|
|
|
Managed_Scope scope = 0;
|
|
|
|
if (view_get_managed_scope(app, view_id, &scope)){
|
|
|
|
Managed_Object ui_data_object = 0;
|
|
|
|
if (managed_variable_get(app, scope, view_ui_data, &ui_data_object)){
|
|
|
|
if (ui_data_object == 0){
|
|
|
|
Managed_Object new_ui_data_object = alloc_managed_memory_in_scope(app, scope, sizeof(UI_Storage), 1);
|
|
|
|
Managed_Object arena_object = alloc_managed_arena_in_scope(app, scope, (8 << 10));
|
|
|
|
Arena *arena = 0;
|
|
|
|
if (managed_object_load_data(app, arena_object, 0, 1, &arena)){
|
|
|
|
UI_Data *ui_data = push_array(arena, UI_Data, 1);
|
|
|
|
UI_Storage storage = {};
|
|
|
|
storage.data = ui_data;
|
|
|
|
storage.arena = arena;
|
|
|
|
storage.arena_object = arena_object;
|
|
|
|
storage.temp = begin_temp_memory(arena);
|
|
|
|
if (managed_object_store_data(app, new_ui_data_object, 0, 1, &storage)){
|
|
|
|
if (managed_variable_set(app, scope, view_ui_data, new_ui_data_object)){
|
|
|
|
ui_data_object = new_ui_data_object;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ui_data_object != 0){
|
|
|
|
UI_Storage storage = {};
|
|
|
|
if (managed_object_load_data(app, ui_data_object, 0, 1, &storage)){
|
|
|
|
*ui_data_out = storage.data;
|
|
|
|
*ui_arena_out = storage.arena;
|
|
|
|
if ((flags & ViewGetUIFlag_ClearData) != 0){
|
|
|
|
end_temp_memory(storage.temp);
|
|
|
|
}
|
|
|
|
result = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static b32
|
|
|
|
view_clear_ui_data(Application_Links *app, View_ID view_id){
|
|
|
|
b32 result = false;
|
|
|
|
Managed_Scope scope = 0;
|
|
|
|
if (view_get_managed_scope(app, view_id, &scope)){
|
|
|
|
Managed_Object ui_data_object = 0;
|
|
|
|
if (managed_variable_get(app, scope, view_ui_data, &ui_data_object)){
|
|
|
|
if (ui_data_object != 0){
|
|
|
|
UI_Storage storage = {};
|
|
|
|
if (managed_object_load_data(app, ui_data_object, 0, 1, &storage)){
|
|
|
|
managed_object_free(app, storage.arena_object);
|
|
|
|
managed_object_free(app, ui_data_object);
|
|
|
|
managed_variable_set(app, scope, view_ui_data, 0);
|
|
|
|
result = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////
|
|
|
|
|
2018-07-14 01:13:05 +00:00
|
|
|
static UI_Item*
|
2019-02-26 19:59:57 +00:00
|
|
|
ui_list_add_item(Arena *arena, UI_List *list, UI_Item item){
|
|
|
|
UI_Item *node = push_array(arena, UI_Item, 1);
|
|
|
|
memcpy(node, &item, sizeof(item));
|
2018-07-14 01:13:05 +00:00
|
|
|
zdll_push_back(list->first, list->last, node);
|
|
|
|
list->count += 1;
|
2019-02-26 19:59:57 +00:00
|
|
|
return(node);
|
2018-07-14 01:13:05 +00:00
|
|
|
}
|
|
|
|
|
2018-08-04 02:41:38 +00:00
|
|
|
static i32_Rect
|
|
|
|
ui__rect_union(i32_Rect a, i32_Rect b){
|
|
|
|
if (b.x1 > b.x0 && b.y1 > b.y0){
|
|
|
|
if (a.x0 > b.x0){
|
|
|
|
a.x0 = b.x0;
|
|
|
|
}
|
|
|
|
if (a.x1 < b.x1){
|
|
|
|
a.x1 = b.x1;
|
|
|
|
}
|
|
|
|
if (a.y0 > b.y0){
|
|
|
|
a.y0 = b.y0;
|
|
|
|
}
|
|
|
|
if (a.y1 < b.y1){
|
|
|
|
a.y1 = b.y1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(a);
|
|
|
|
}
|
|
|
|
|
2019-02-26 19:59:57 +00:00
|
|
|
static void
|
|
|
|
ui_data_compute_bounding_boxes(UI_Data *ui_data){
|
2018-11-20 08:18:54 +00:00
|
|
|
i32_Rect neg_inf_rect = {};
|
2018-08-04 02:41:38 +00:00
|
|
|
neg_inf_rect.x0 = INT32_MAX;
|
|
|
|
neg_inf_rect.y0 = INT32_MAX;
|
|
|
|
neg_inf_rect.x1 = INT32_MIN;
|
|
|
|
neg_inf_rect.y1 = INT32_MIN;
|
|
|
|
for (uint32_t i = 0; i < UICoordinates_COUNT; ++i){
|
2019-02-26 19:59:57 +00:00
|
|
|
ui_data->bounding_box[i] = neg_inf_rect;
|
2018-07-17 03:21:22 +00:00
|
|
|
}
|
2019-02-26 19:59:57 +00:00
|
|
|
for (UI_Item *item = ui_data->list.first;
|
|
|
|
item != 0;
|
|
|
|
item = item->next){
|
2018-08-04 02:41:38 +00:00
|
|
|
if (item->coordinates >= UICoordinates_COUNT){
|
2019-02-25 23:42:13 +00:00
|
|
|
item->coordinates = UICoordinates_ViewSpace;
|
2018-07-17 03:21:22 +00:00
|
|
|
}
|
2019-02-26 19:59:57 +00:00
|
|
|
Rect_i32 *box = &ui_data->bounding_box[item->coordinates];
|
|
|
|
*box = ui__rect_union(*box, item->rect_outer);
|
2018-07-14 01:13:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-17 03:21:22 +00:00
|
|
|
static void
|
2019-02-26 19:59:57 +00:00
|
|
|
ui_control_set_top(UI_Data *data, int32_t top_y){
|
|
|
|
data->bounding_box[UICoordinates_ViewSpace].y0 = top_y;
|
2018-07-17 03:21:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-02-26 19:59:57 +00:00
|
|
|
ui_control_set_bottom(UI_Data *data, int32_t bottom_y){
|
|
|
|
data->bounding_box[UICoordinates_ViewSpace].y1 = bottom_y;
|
2018-07-17 03:21:22 +00:00
|
|
|
}
|
|
|
|
|
2018-07-14 01:13:05 +00:00
|
|
|
static UI_Item*
|
2019-02-26 19:59:57 +00:00
|
|
|
ui_control_get_mouse_hit(UI_Data *data, Vec2_i32 view_p, Vec2_i32 panel_p){
|
2019-02-25 23:42:13 +00:00
|
|
|
UI_Item *result = 0;
|
2019-02-26 19:59:57 +00:00
|
|
|
for (UI_Item *item = data->list.first;
|
|
|
|
item != 0 && result == 0;
|
|
|
|
item = item->next){
|
|
|
|
i32_Rect r = item->rect_outer;
|
2018-08-04 02:41:38 +00:00
|
|
|
switch (item->coordinates){
|
2019-02-25 23:42:13 +00:00
|
|
|
case UICoordinates_ViewSpace:
|
2018-08-04 02:41:38 +00:00
|
|
|
{
|
2019-02-25 23:42:13 +00:00
|
|
|
if (hit_check(r, view_p)){
|
|
|
|
result = item;
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
}break;
|
2019-02-25 23:42:13 +00:00
|
|
|
case UICoordinates_PanelSpace:
|
2018-08-04 02:41:38 +00:00
|
|
|
{
|
2019-02-25 23:42:13 +00:00
|
|
|
if (hit_check(r, panel_p)){
|
|
|
|
result = item;
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
}break;
|
|
|
|
}
|
|
|
|
}
|
2019-02-25 23:42:13 +00:00
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static UI_Item*
|
2019-02-26 19:59:57 +00:00
|
|
|
ui_control_get_mouse_hit(UI_Data *data, int32_t mx_scrolled, int32_t my_scrolled, int32_t mx_unscrolled, int32_t my_unscrolled){
|
|
|
|
return(ui_control_get_mouse_hit(data, V2i32(mx_scrolled, my_scrolled), V2i32(mx_unscrolled, my_unscrolled)));
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////
|
|
|
|
|
|
|
|
static void
|
|
|
|
view_zero_scroll(Application_Links *app, View_Summary *view){
|
2018-11-20 08:18:54 +00:00
|
|
|
GUI_Scroll_Vars zero_scroll = {};
|
2018-08-04 02:41:38 +00:00
|
|
|
view_set_scroll(app, view, zero_scroll);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
view_set_vertical_focus(Application_Links *app, View_Summary *view,
|
|
|
|
int32_t y_top, int32_t y_bot){
|
|
|
|
GUI_Scroll_Vars scroll = view->scroll_vars;
|
|
|
|
int32_t view_y_top = scroll.target_y;
|
|
|
|
int32_t view_y_dim = view->file_region.y1 - view->file_region.y0;
|
|
|
|
int32_t view_y_bot = view_y_top + view_y_dim;
|
|
|
|
int32_t line_dim = (int32_t)view->line_height;
|
|
|
|
int32_t hot_y_top = view_y_top + line_dim*3;
|
|
|
|
int32_t hot_y_bot = view_y_bot - line_dim*3;
|
|
|
|
if (hot_y_bot - hot_y_top < line_dim*6){
|
|
|
|
int32_t quarter_view_y_dim = view_y_dim/4;
|
|
|
|
hot_y_top = view_y_top + quarter_view_y_dim;
|
|
|
|
hot_y_bot = view_y_bot - quarter_view_y_dim;
|
|
|
|
}
|
|
|
|
int32_t hot_y_dim = hot_y_bot - hot_y_top;
|
|
|
|
int32_t skirt_dim = hot_y_top - view_y_top;
|
|
|
|
int32_t y_dim = y_bot - y_top;
|
|
|
|
if (y_dim > hot_y_dim){
|
|
|
|
scroll.target_y = y_top - skirt_dim;
|
|
|
|
view_set_scroll(app, view, scroll);
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
if (y_top < hot_y_top){
|
|
|
|
scroll.target_y = y_top - skirt_dim;
|
|
|
|
view_set_scroll(app, view, scroll);
|
|
|
|
}
|
|
|
|
else if (y_bot > hot_y_bot){
|
|
|
|
scroll.target_y = y_bot + skirt_dim - view_y_dim;
|
|
|
|
view_set_scroll(app, view, scroll);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-25 23:42:13 +00:00
|
|
|
static Vec2
|
|
|
|
view_space_from_screen_space(Vec2 p, Vec2 file_region_p0, Vec2 scroll_p){
|
|
|
|
return(p - file_region_p0 + scroll_p);
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
|
2019-02-25 23:42:13 +00:00
|
|
|
static Vec2_i32
|
|
|
|
view_space_from_screen_space(Vec2_i32 p, Vec2_i32 file_region_p0, Vec2_i32 scroll_p){
|
|
|
|
return(p - file_region_p0 + scroll_p);
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
|
2019-02-25 23:42:13 +00:00
|
|
|
static Vec2_i32
|
|
|
|
get_mouse_position_in_view_space(Mouse_State mouse, Vec2_i32 file_region_p0, Vec2_i32 scroll_p){
|
|
|
|
return(view_space_from_screen_space(mouse.p, file_region_p0, scroll_p));
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
|
2019-02-25 23:42:13 +00:00
|
|
|
static Vec2_i32
|
|
|
|
get_mouse_position_in_view_space(Application_Links *app, Vec2_i32 file_region_p0, Vec2_i32 scroll_p){
|
|
|
|
return(get_mouse_position_in_view_space(get_mouse_state(app), file_region_p0, scroll_p));
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
|
2019-02-25 23:42:13 +00:00
|
|
|
static Vec2
|
|
|
|
panel_space_from_screen_space(Vec2 p, Vec2 file_region_p0){
|
|
|
|
return(p - file_region_p0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Vec2_i32
|
|
|
|
panel_space_from_screen_space(Vec2_i32 p, Vec2_i32 file_region_p0){
|
|
|
|
return(p - file_region_p0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Vec2_i32
|
|
|
|
get_mouse_position_in_panel_space(Mouse_State mouse, Vec2_i32 file_region_p0){
|
|
|
|
return(panel_space_from_screen_space(mouse.p, file_region_p0));
|
|
|
|
}
|
|
|
|
|
|
|
|
static Vec2_i32
|
|
|
|
get_mouse_position_in_panel_space(Application_Links *app, Vec2_i32 file_region_p0){
|
|
|
|
return(get_mouse_position_in_panel_space(get_mouse_state(app), file_region_p0));
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////
|
|
|
|
|
|
|
|
Lister_State global_lister_state_[16] = {};
|
|
|
|
Lister_State *global_lister_state = global_lister_state_ - 1;
|
|
|
|
|
|
|
|
static Lister_State*
|
|
|
|
view_get_lister_state(View_Summary *view){
|
|
|
|
return(&global_lister_state[view->view_id]);
|
|
|
|
}
|
|
|
|
|
2018-12-17 02:07:49 +00:00
|
|
|
static int32_t
|
|
|
|
lister_standard_arena_size_round_up(int32_t arena_size){
|
|
|
|
if (arena_size < (64 << 10)){
|
|
|
|
arena_size = (64 << 10);
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
arena_size += (4 << 10) - 1;
|
|
|
|
arena_size -= arena_size%(4 << 10);
|
|
|
|
}
|
|
|
|
return(arena_size);
|
|
|
|
}
|
|
|
|
|
2018-08-04 02:41:38 +00:00
|
|
|
static void
|
2018-12-17 02:07:49 +00:00
|
|
|
init_lister_state(Application_Links *app, Lister_State *state, Heap *heap){
|
2018-08-04 02:41:38 +00:00
|
|
|
state->initialized = true;
|
|
|
|
state->hot_user_data = 0;
|
|
|
|
state->item_index = 0;
|
|
|
|
state->set_view_vertical_focus_to_item = false;
|
2018-12-17 02:07:49 +00:00
|
|
|
state->item_count_after_filter = 0;
|
2019-02-05 00:10:38 +00:00
|
|
|
arena_release_all(&state->lister.arena);
|
2018-08-04 02:41:38 +00:00
|
|
|
memset(&state->lister, 0, sizeof(state->lister));
|
|
|
|
}
|
|
|
|
|
2018-09-08 01:36:42 +00:00
|
|
|
UI_QUIT_FUNCTION(lister_quit_function){
|
|
|
|
Lister_State *state = view_get_lister_state(&view);
|
|
|
|
state->initialized = false;
|
2019-02-26 19:59:57 +00:00
|
|
|
view_clear_ui_data(app, view.view_id);
|
2018-09-08 01:36:42 +00:00
|
|
|
}
|
|
|
|
|
2018-08-04 02:41:38 +00:00
|
|
|
static UI_Item
|
2019-02-26 19:59:57 +00:00
|
|
|
lister_get_clicked_item(Application_Links *app, View_ID view_id, Partition *scratch){
|
2018-11-20 08:18:54 +00:00
|
|
|
UI_Item result = {};
|
2019-02-26 19:59:57 +00:00
|
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
|
|
UI_Data *ui_data = 0;
|
|
|
|
Arena *ui_arena = 0;
|
|
|
|
if (view_get_ui_data(app, view_id, ViewGetUIFlag_KeepDataAsIs, &ui_data, &ui_arena)){
|
|
|
|
View_Summary view = {};
|
|
|
|
get_view_summary(app, view_id, AccessAll, &view);
|
|
|
|
Mouse_State mouse = get_mouse_state(app);
|
|
|
|
Vec2_i32 region_p0 = view.file_region.p0;
|
|
|
|
Vec2_i32 m_view_space = get_mouse_position_in_view_space(mouse, region_p0, V2i32(view.scroll_vars.scroll_p));
|
|
|
|
Vec2_i32 m_panel_space = get_mouse_position_in_panel_space(mouse, region_p0);
|
|
|
|
UI_Item *clicked = ui_control_get_mouse_hit(ui_data, m_view_space, m_panel_space);
|
|
|
|
if (clicked != 0){
|
|
|
|
result = *clicked;
|
|
|
|
}
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
end_temp_memory(temp);
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
2018-12-17 02:07:49 +00:00
|
|
|
static int32_t
|
|
|
|
lister_get_line_height(View_Summary *view){
|
|
|
|
return((int32_t)view->line_height);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int32_t
|
|
|
|
lister_get_text_field_height(View_Summary *view){
|
|
|
|
return((int32_t)view->line_height);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int32_t
|
|
|
|
lister_get_block_height(int32_t line_height, bool32 is_theme_list){
|
2018-08-10 21:52:57 +00:00
|
|
|
int32_t block_height = 0;
|
|
|
|
if (is_theme_list){
|
|
|
|
block_height = line_height*3 + 6;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
block_height = line_height*2;
|
|
|
|
}
|
2018-12-17 02:07:49 +00:00
|
|
|
return(block_height);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-02-26 19:59:57 +00:00
|
|
|
lister_update_ui(Application_Links *app, Partition *scratch, View_Summary *view, Lister_State *state){
|
2018-12-17 02:07:49 +00:00
|
|
|
bool32 is_theme_list = state->lister.data.theme_list;
|
|
|
|
|
|
|
|
int32_t x0 = 0;
|
|
|
|
int32_t x1 = view->view_region.x1 - view->view_region.x0;
|
|
|
|
int32_t line_height = lister_get_line_height(view);
|
|
|
|
int32_t block_height = lister_get_block_height(line_height, is_theme_list);
|
|
|
|
int32_t text_field_height = lister_get_text_field_height(view);
|
2018-08-04 02:41:38 +00:00
|
|
|
|
|
|
|
Temp_Memory full_temp = begin_temp_memory(scratch);
|
|
|
|
|
|
|
|
refresh_view(app, view);
|
2019-02-25 23:42:13 +00:00
|
|
|
Vec2_i32 view_m = get_mouse_position_in_view_space(app, view->file_region.p0,
|
|
|
|
V2i32(view->scroll_vars.scroll_p));
|
2018-08-04 02:41:38 +00:00
|
|
|
|
2018-12-17 02:07:49 +00:00
|
|
|
int32_t y_pos = text_field_height;
|
2018-08-04 02:41:38 +00:00
|
|
|
|
|
|
|
state->raw_item_index = -1;
|
|
|
|
|
2018-12-17 02:07:49 +00:00
|
|
|
int32_t node_count = state->lister.data.options.count;
|
2018-11-20 08:18:54 +00:00
|
|
|
Lister_Node_Ptr_Array exact_matches = {};
|
2018-09-15 23:48:02 +00:00
|
|
|
exact_matches.node_ptrs = push_array(scratch, Lister_Node*, 1);
|
2018-11-20 08:18:54 +00:00
|
|
|
Lister_Node_Ptr_Array before_extension_matches = {};
|
2018-09-15 23:48:02 +00:00
|
|
|
before_extension_matches.node_ptrs = push_array(scratch, Lister_Node*, node_count);
|
2018-11-20 08:18:54 +00:00
|
|
|
Lister_Node_Ptr_Array substring_matches = {};
|
2018-09-15 23:48:02 +00:00
|
|
|
substring_matches.node_ptrs = push_array(scratch, Lister_Node*, node_count);
|
2018-09-17 18:47:06 +00:00
|
|
|
|
2018-12-17 02:07:49 +00:00
|
|
|
String key = state->lister.data.key_string;
|
2018-11-20 08:18:54 +00:00
|
|
|
Absolutes absolutes = {};
|
2018-10-06 01:42:56 +00:00
|
|
|
get_absolutes(key, &absolutes, true, true);
|
|
|
|
bool32 has_wildcard = (absolutes.count > 3);
|
2018-09-17 18:47:06 +00:00
|
|
|
|
2018-12-17 02:07:49 +00:00
|
|
|
for (Lister_Node *node = state->lister.data.options.first;
|
2018-09-14 19:02:02 +00:00
|
|
|
node != 0;
|
|
|
|
node = node->next){
|
2018-10-06 01:42:56 +00:00
|
|
|
if (key.size == 0 ||
|
2018-09-17 18:47:06 +00:00
|
|
|
wildcard_match_s(&absolutes, node->string, false)){
|
2018-10-06 01:42:56 +00:00
|
|
|
if (match_insensitive(node->string, key) && exact_matches.count == 0){
|
2018-09-14 19:02:02 +00:00
|
|
|
exact_matches.node_ptrs[exact_matches.count++] = node;
|
|
|
|
}
|
2018-09-17 18:47:06 +00:00
|
|
|
else if (!has_wildcard &&
|
2018-10-06 01:42:56 +00:00
|
|
|
match_part_insensitive(node->string, key) &&
|
|
|
|
node->string.size > key.size &&
|
|
|
|
node->string.str[key.size] == '.'){
|
2018-09-14 19:02:02 +00:00
|
|
|
before_extension_matches.node_ptrs[before_extension_matches.count++] = node;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
substring_matches.node_ptrs[substring_matches.count++] = node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-15 23:48:02 +00:00
|
|
|
Lister_Node_Ptr_Array node_ptr_arrays[] = {
|
2018-09-14 19:02:02 +00:00
|
|
|
exact_matches,
|
|
|
|
before_extension_matches,
|
|
|
|
substring_matches,
|
|
|
|
};
|
|
|
|
|
2019-02-26 19:59:57 +00:00
|
|
|
UI_Data *ui_data = 0;
|
|
|
|
Arena *ui_arena = 0;
|
|
|
|
if (view_get_ui_data(app, view->view_id, ViewGetUIFlag_ClearData, &ui_data, &ui_arena)){
|
|
|
|
memset(ui_data, 0, sizeof(*ui_data));
|
|
|
|
|
|
|
|
UI_Item *highlighted_item = 0;
|
|
|
|
UI_Item *hot_item = 0;
|
|
|
|
UI_Item *hovered_item = 0;
|
|
|
|
int32_t item_index_counter = 0;
|
|
|
|
for (int32_t array_index = 0; array_index < ArrayCount(node_ptr_arrays); array_index += 1){
|
|
|
|
Lister_Node_Ptr_Array node_ptr_array = node_ptr_arrays[array_index];
|
|
|
|
for (int32_t node_index = 0; node_index < node_ptr_array.count; node_index += 1){
|
|
|
|
Lister_Node *node = node_ptr_array.node_ptrs[node_index];
|
|
|
|
|
|
|
|
i32_Rect item_rect = {};
|
|
|
|
item_rect.x0 = x0;
|
|
|
|
item_rect.y0 = y_pos;
|
|
|
|
item_rect.x1 = x1;
|
|
|
|
item_rect.y1 = y_pos + block_height;
|
|
|
|
y_pos = item_rect.y1;
|
|
|
|
|
|
|
|
UI_Item item = {};
|
|
|
|
item.activation_level = UIActivation_None;
|
|
|
|
item.coordinates = UICoordinates_ViewSpace;
|
|
|
|
item.rect_outer = item_rect;
|
|
|
|
item.inner_margin = 3;
|
|
|
|
|
|
|
|
if (!is_theme_list){
|
|
|
|
Fancy_String_List list = {};
|
|
|
|
push_fancy_string (ui_arena, &list, fancy_id(Stag_Default), node->string);
|
|
|
|
push_fancy_stringf(ui_arena, &list, fancy_id(Stag_Default), " ");
|
|
|
|
push_fancy_string (ui_arena, &list, fancy_id(Stag_Pop2 ), node->status);
|
|
|
|
item.lines[0] = list;
|
|
|
|
item.line_count = 1;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
//i32 style_index = node->index;
|
|
|
|
|
|
|
|
String name = make_lit_string("name");
|
|
|
|
item.lines[0] = fancy_string_list_single(push_fancy_string(ui_arena, fancy_id(Stag_Default), name));
|
|
|
|
|
|
|
|
Fancy_String_List list = {};
|
|
|
|
push_fancy_stringf(ui_arena, &list, fancy_id(Stag_Keyword ), "if ");
|
|
|
|
push_fancy_stringf(ui_arena, &list, fancy_id(Stag_Default ), "(x < ");
|
|
|
|
push_fancy_stringf(ui_arena, &list, fancy_id(Stag_Int_Constant), "0");
|
|
|
|
push_fancy_stringf(ui_arena, &list, fancy_id(Stag_Default ), ") { x = ");
|
|
|
|
push_fancy_stringf(ui_arena, &list, fancy_id(Stag_Int_Constant), "0");
|
|
|
|
push_fancy_stringf(ui_arena, &list, fancy_id(Stag_Default ), "; } ");
|
|
|
|
push_fancy_stringf(ui_arena, &list, fancy_id(Stag_Comment ), "// comment");
|
|
|
|
item.lines[1] = list;
|
|
|
|
|
|
|
|
item.line_count = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
item.user_data = node->user_data;
|
|
|
|
|
|
|
|
|
|
|
|
UI_Item *item_ptr = ui_list_add_item(ui_arena, &ui_data->list, item);
|
|
|
|
if (hit_check(item_rect, view_m)){
|
|
|
|
hovered_item = item_ptr;
|
|
|
|
}
|
|
|
|
if (state->item_index == item_index_counter){
|
|
|
|
highlighted_item = item_ptr;
|
|
|
|
state->raw_item_index = node->raw_index;
|
|
|
|
}
|
|
|
|
item_index_counter += 1;
|
|
|
|
if (node->user_data == state->hot_user_data && hot_item != 0){
|
|
|
|
hot_item = item_ptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
state->item_count_after_filter = item_index_counter;
|
|
|
|
|
|
|
|
if (hovered_item != 0){
|
|
|
|
hovered_item->activation_level = UIActivation_Hover;
|
|
|
|
}
|
|
|
|
if (hot_item != 0){
|
|
|
|
if (hot_item == hovered_item){
|
|
|
|
hot_item->activation_level = UIActivation_Active;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
hot_item->activation_level = UIActivation_Hover;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (highlighted_item != 0){
|
|
|
|
highlighted_item->activation_level = UIActivation_Active;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->set_view_vertical_focus_to_item){
|
|
|
|
if (highlighted_item != 0){
|
|
|
|
view_set_vertical_focus(app, view, highlighted_item->rect_outer.y0, highlighted_item->rect_outer.y1);
|
|
|
|
}
|
|
|
|
state->set_view_vertical_focus_to_item = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2018-11-20 08:18:54 +00:00
|
|
|
i32_Rect item_rect = {};
|
2018-08-04 02:41:38 +00:00
|
|
|
item_rect.x0 = x0;
|
2019-02-26 19:59:57 +00:00
|
|
|
item_rect.y0 = 0;
|
2018-08-04 02:41:38 +00:00
|
|
|
item_rect.x1 = x1;
|
2019-02-26 19:59:57 +00:00
|
|
|
item_rect.y1 = item_rect.y0 + text_field_height;
|
2018-08-04 02:41:38 +00:00
|
|
|
y_pos = item_rect.y1;
|
|
|
|
|
2018-11-20 08:18:54 +00:00
|
|
|
UI_Item item = {};
|
2019-02-26 19:59:57 +00:00
|
|
|
item.activation_level = UIActivation_Active;
|
|
|
|
item.coordinates = UICoordinates_PanelSpace;
|
|
|
|
item.rect_outer = item_rect;
|
|
|
|
item.inner_margin = 0;
|
|
|
|
{
|
|
|
|
Fancy_String_List list = {};
|
|
|
|
push_fancy_string (ui_arena, &list, fancy_id(Stag_Pop1 ), state->lister.data.query);
|
|
|
|
push_fancy_stringf(ui_arena, &list, fancy_id(Stag_Pop1 ), " ");
|
|
|
|
push_fancy_string (ui_arena, &list, fancy_id(Stag_Default), state->lister.data.text_field);
|
|
|
|
item.lines[0] = list;
|
|
|
|
item.line_count = 1;
|
2018-08-10 21:52:57 +00:00
|
|
|
}
|
2019-02-26 19:59:57 +00:00
|
|
|
item.user_data = 0;
|
2018-08-04 02:41:38 +00:00
|
|
|
|
2019-02-26 19:59:57 +00:00
|
|
|
|
|
|
|
ui_list_add_item(ui_arena, &ui_data->list, item);
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
|
2019-02-26 19:59:57 +00:00
|
|
|
ui_data_compute_bounding_boxes(ui_data);
|
|
|
|
|
|
|
|
// TODO(allen): what to do with control now?
|
|
|
|
//UI_Control control = ui_list_to_ui_control(scratch, &list);
|
|
|
|
view_set_quit_ui_handler(app, view->view_id, lister_quit_function);
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
end_temp_memory(full_temp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Lister_Prealloced_String
|
|
|
|
lister_prealloced(String string){
|
2018-11-20 08:18:54 +00:00
|
|
|
Lister_Prealloced_String result = {};
|
2018-08-04 02:41:38 +00:00
|
|
|
result.string = string;
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-02-05 00:10:38 +00:00
|
|
|
lister_first_init(Application_Links *app, Lister *lister, void *user_data, int32_t user_data_size){
|
2018-08-04 02:41:38 +00:00
|
|
|
memset(lister, 0, sizeof(*lister));
|
2019-02-05 00:10:38 +00:00
|
|
|
lister->arena = make_arena(app, (16 << 10));
|
2018-12-17 02:07:49 +00:00
|
|
|
lister->data.query = make_fixed_width_string(lister->data.query_space);
|
|
|
|
lister->data.text_field = make_fixed_width_string(lister->data.text_field_space);
|
|
|
|
lister->data.key_string = make_fixed_width_string(lister->data.key_string_space);
|
2019-02-05 00:10:38 +00:00
|
|
|
lister->data.user_data = push_array(&lister->arena, char, user_data_size);
|
|
|
|
lister->data.user_data_size = user_data_size;
|
|
|
|
push_align(&lister->arena, 8);
|
|
|
|
if (user_data != 0){
|
|
|
|
memcpy(lister->data.user_data, user_data, user_data_size);
|
2018-09-15 23:48:02 +00:00
|
|
|
}
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_begin_new_item_set(Application_Links *app, Lister *lister, int32_t list_memory_size){
|
2019-02-05 00:10:38 +00:00
|
|
|
arena_release_all(&lister->arena);
|
2018-12-17 02:07:49 +00:00
|
|
|
memset(&lister->data.options, 0, sizeof(lister->data.options));
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
|
2018-08-05 07:09:18 +00:00
|
|
|
static void*
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_add_item(Lister *lister, Lister_Prealloced_String string, Lister_Prealloced_String status,
|
2018-08-05 07:09:18 +00:00
|
|
|
void *user_data, int32_t extra_space){
|
2018-12-17 02:07:49 +00:00
|
|
|
Lister_Node *node = push_array(&lister->arena, Lister_Node, 1);
|
2018-08-04 02:41:38 +00:00
|
|
|
node->string = string.string;
|
|
|
|
node->status = status.string;
|
|
|
|
node->user_data = user_data;
|
2018-12-17 02:07:49 +00:00
|
|
|
node->raw_index = lister->data.options.count;
|
|
|
|
zdll_push_back(lister->data.options.first, lister->data.options.last, node);
|
|
|
|
lister->data.options.count += 1;
|
|
|
|
void *result = push_array(&lister->arena, char, extra_space);
|
|
|
|
push_align(&lister->arena, 8);
|
2018-08-05 07:09:18 +00:00
|
|
|
return(result);
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
|
2018-08-05 07:09:18 +00:00
|
|
|
static void*
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_add_item(Lister *lister, Lister_Prealloced_String string, String status,
|
2018-08-05 07:09:18 +00:00
|
|
|
void *user_data, int32_t extra_space){
|
2018-12-17 02:07:49 +00:00
|
|
|
return(lister_add_item(lister, string, lister_prealloced(string_push_copy(&lister->arena, status)),
|
2018-08-05 07:09:18 +00:00
|
|
|
user_data, extra_space));
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
|
2018-08-05 07:09:18 +00:00
|
|
|
static void*
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_add_item(Lister *lister, String string, Lister_Prealloced_String status,
|
2018-08-05 07:09:18 +00:00
|
|
|
void *user_data, int32_t extra_space){
|
2018-12-17 02:07:49 +00:00
|
|
|
return(lister_add_item(lister, lister_prealloced(string_push_copy(&lister->arena, string)), status,
|
2018-08-05 07:09:18 +00:00
|
|
|
user_data, extra_space));
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
|
2018-08-05 07:09:18 +00:00
|
|
|
static void*
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_add_item(Lister *lister, String string, String status, void *user_data, int32_t extra_space){
|
|
|
|
return(lister_add_item(lister,
|
|
|
|
lister_prealloced(string_push_copy(&lister->arena, string)),
|
|
|
|
lister_prealloced(string_push_copy(&lister->arena, status)),
|
2018-08-05 07:09:18 +00:00
|
|
|
user_data, extra_space));
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
|
2018-08-10 21:52:57 +00:00
|
|
|
static void*
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_add_theme_item(Lister *lister,
|
|
|
|
Lister_Prealloced_String string, int32_t index,
|
|
|
|
void *user_data, int32_t extra_space){
|
|
|
|
Lister_Node *node = push_array(&lister->arena, Lister_Node, 1);
|
2018-08-10 21:52:57 +00:00
|
|
|
node->string = string.string;
|
|
|
|
node->index = index;
|
|
|
|
node->user_data = user_data;
|
2018-12-17 02:07:49 +00:00
|
|
|
node->raw_index = lister->data.options.count;
|
|
|
|
zdll_push_back(lister->data.options.first, lister->data.options.last, node);
|
|
|
|
lister->data.options.count += 1;
|
|
|
|
void *result = push_array(&lister->arena, char, extra_space);
|
|
|
|
push_align(&lister->arena, 8);
|
2018-08-10 21:52:57 +00:00
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void*
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_add_theme_item(Lister *lister, String string, int32_t index,
|
|
|
|
void *user_data, int32_t extra_space){
|
|
|
|
return(lister_add_theme_item(lister, lister_prealloced(string_push_copy(&lister->arena, string)), index,
|
|
|
|
user_data, extra_space));
|
2018-08-10 21:52:57 +00:00
|
|
|
}
|
|
|
|
|
2018-08-04 02:41:38 +00:00
|
|
|
static void*
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_get_user_data(Lister_Data *lister_data, int32_t index){
|
2019-02-26 19:59:57 +00:00
|
|
|
void *result = 0;
|
2018-12-17 02:07:49 +00:00
|
|
|
if (0 <= index && index < lister_data->options.count){
|
2018-08-04 02:41:38 +00:00
|
|
|
int32_t counter = 0;
|
2018-12-17 02:07:49 +00:00
|
|
|
for (Lister_Node *node = lister_data->options.first;
|
2018-08-04 02:41:38 +00:00
|
|
|
node != 0;
|
2019-02-26 19:59:57 +00:00
|
|
|
node = node->next, counter += 1){
|
2018-08-04 02:41:38 +00:00
|
|
|
if (counter == index){
|
2019-02-26 19:59:57 +00:00
|
|
|
result = node->user_data;
|
|
|
|
break;
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
}
|
2018-07-14 01:13:05 +00:00
|
|
|
}
|
2019-02-26 19:59:57 +00:00
|
|
|
return(result);
|
2018-07-14 01:13:05 +00:00
|
|
|
}
|
|
|
|
|
2018-08-04 02:41:38 +00:00
|
|
|
static void
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_call_refresh_handler(Application_Links *app, Lister *lister){
|
|
|
|
if (lister->data.handlers.refresh != 0){
|
|
|
|
lister->data.handlers.refresh(app, lister);
|
2018-08-04 02:41:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-09-17 18:47:06 +00:00
|
|
|
lister_default(Application_Links *app, Partition *scratch, Heap *heap,
|
|
|
|
View_Summary *view, Lister_State *state,
|
|
|
|
Lister_Activation_Code code){
|
2018-08-04 02:41:38 +00:00
|
|
|
switch (code){
|
|
|
|
case ListerActivation_Finished:
|
|
|
|
{
|
2018-09-14 19:02:02 +00:00
|
|
|
view_end_ui_mode(app, view);
|
|
|
|
state->initialized = false;
|
2019-02-05 00:10:38 +00:00
|
|
|
arena_release_all(&state->lister.arena);
|
2018-08-04 02:41:38 +00:00
|
|
|
}break;
|
|
|
|
|
2018-09-15 23:48:02 +00:00
|
|
|
case ListerActivation_Continue:
|
|
|
|
{
|
2018-10-01 19:32:28 +00:00
|
|
|
view_begin_ui_mode(app, view);
|
2018-09-15 23:48:02 +00:00
|
|
|
}break;
|
|
|
|
|
2018-08-04 02:41:38 +00:00
|
|
|
case ListerActivation_ContinueAndRefresh:
|
|
|
|
{
|
2018-10-01 19:32:28 +00:00
|
|
|
view_begin_ui_mode(app, view);
|
2018-08-04 02:41:38 +00:00
|
|
|
state->item_index = 0;
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_call_refresh_handler(app, &state->lister);
|
2018-08-04 02:41:38 +00:00
|
|
|
lister_update_ui(app, scratch, view, state);
|
|
|
|
}break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-17 18:47:06 +00:00
|
|
|
static void
|
|
|
|
lister_call_activate_handler(Application_Links *app, Partition *scratch, Heap *heap,
|
|
|
|
View_Summary *view, Lister_State *state,
|
|
|
|
void *user_data, bool32 activated_by_mouse){
|
2018-12-17 02:07:49 +00:00
|
|
|
Lister_Data *lister = &state->lister.data;
|
2018-09-17 18:47:06 +00:00
|
|
|
if (lister->handlers.activate != 0){
|
|
|
|
lister->handlers.activate(app, scratch, heap, view, state,
|
|
|
|
lister->text_field, user_data, activated_by_mouse);
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
lister_default(app, scratch, heap, view, state, ListerActivation_Finished);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-04 02:41:38 +00:00
|
|
|
static void
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_set_query_string(Lister_Data *lister, char *string){
|
2018-08-04 02:41:38 +00:00
|
|
|
copy(&lister->query, string);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_set_query_string(Lister_Data *lister, String string){
|
2018-08-04 02:41:38 +00:00
|
|
|
copy(&lister->query, string);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_set_text_field_string(Lister_Data *lister, char *string){
|
2018-08-04 02:41:38 +00:00
|
|
|
copy(&lister->text_field, string);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_set_text_field_string(Lister_Data *lister, String string){
|
2018-08-04 02:41:38 +00:00
|
|
|
copy(&lister->text_field, string);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_set_key_string(Lister_Data *lister, char *string){
|
2018-08-04 02:41:38 +00:00
|
|
|
copy(&lister->key_string, string);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-12-17 02:07:49 +00:00
|
|
|
lister_set_key_string(Lister_Data *lister, String string){
|
2018-08-04 02:41:38 +00:00
|
|
|
copy(&lister->key_string, string);
|
|
|
|
}
|
|
|
|
|
2018-07-14 01:13:05 +00:00
|
|
|
// BOTTOM
|
|
|
|
|