/* * Mr. 4th Dimention - Allen Webster * * 19.08.2015 * * Panel layout functions * */ // TOP internal Panel_Split make_panel_split(Panel_Split_Kind kind, i32 v){ Panel_Split split = {}; split.kind = kind; split.v_i32 = v; return(split); } internal Panel_Split make_panel_split(Panel_Split_Kind kind, f32 v){ Panel_Split split = {}; split.kind = kind; split.v_f32 = v; return(split); } internal Panel_Split make_panel_split_50_50(void){ return(make_panel_split(PanelSplitKind_Ratio_Min, 0.5f)); } internal Panel* layout__alloc_panel(Layout *layout){ Panel *panel = 0; Node *node = layout->free_panels.next; if (node != &layout->free_panels){ dll_remove(node); panel = CastFromMember(Panel, node, node); } return(panel); } internal void layout__free_panel(Layout *layout, Panel *panel){ Assert(panel != layout->active_panel); Assert(panel != layout->root); dll_remove(&panel->node); dll_insert(&layout->free_panels, &panel->node); panel->kind = PanelKind_Unused; } internal void layout__set_panel_rectangle(Layout *layout, Panel *panel, i32_Rect rect){ panel->rect_full = rect; panel->rect_inner = get_inner_rect(rect, layout->margin); } internal i32 layout__evaluate_split(Panel_Split split, i32 v0, i32 v1){ i32 v = 0; switch (split.kind){ case PanelSplitKind_Ratio_Min: { v = round32(lerp((f32)v0, split.v_f32, (f32)v1)); }break; case PanelSplitKind_Ratio_Max: { v = round32(lerp((f32)v1, split.v_f32, (f32)v0)); }break; case PanelSplitKind_FixedPixels_Min: { v = clamp_top(v0 + split.v_i32, v1); }break; case PanelSplitKind_FixedPixels_Max: { v = clamp_bottom(v0, v1 - split.v_i32); }break; } return(v); } internal void layout_propogate_sizes_down_from_node(Layout *layout, Panel *panel){ if (panel->kind == PanelKind_Intermediate){ Panel *tl_panel = panel->tl_panel; Panel *br_panel = panel->br_panel; i32_Rect r1 = panel->rect_full; i32_Rect r2 = panel->rect_full; if (panel->vertical_split){ i32 x_pos = layout__evaluate_split(panel->split, r1.x0, r1.x1); r1.x1 = x_pos; r2.x0 = x_pos; } else{ i32 y_pos = layout__evaluate_split(panel->split, r1.y0, r1.y1); r1.y1 = y_pos; r2.y0 = y_pos; } layout__set_panel_rectangle(layout, tl_panel, r1); layout__set_panel_rectangle(layout, br_panel, r2); layout_propogate_sizes_down_from_node(layout, tl_panel); layout_propogate_sizes_down_from_node(layout, br_panel); } } internal i32 layout_get_open_panel_count(Layout *layout){ return(layout->open_panel_count); } internal Panel* layout_get_first_open_panel(Layout *layout){ Panel *panel = CastFromMember(Panel, node, layout->open_panels.next); if (panel != 0 && &panel->node == &layout->open_panels){ panel = 0; } AssertImplies(panel != 0, panel->kind == PanelKind_Final); return(panel); } internal Panel* layout_get_next_open_panel(Layout *layout, Panel *panel){ panel = CastFromMember(Panel, node, panel->node.next); if (&panel->node == &layout->open_panels){ panel = 0; } AssertImplies(panel != 0, panel->kind == PanelKind_Final); return(panel); } internal Panel* layout_get_prev_open_panel(Layout *layout, Panel *panel){ panel = CastFromMember(Panel, node, panel->node.prev); if (&panel->node == &layout->open_panels){ panel = 0; } AssertImplies(panel != 0, panel->kind == PanelKind_Final); return(panel); } internal Panel* layout_get_active_panel(Layout *layout){ return(layout->active_panel); } internal b32 layout_split_panel(Layout *layout, Panel *panel, b32 vertical_split, Panel **new_panel_out){ b32 result = false; if (layout->open_panel_count < layout->open_panel_max_count){ Panel *min_panel = layout__alloc_panel(layout); Panel *max_panel = layout__alloc_panel(layout); dll_remove(&panel->node); dll_insert(&layout->intermediate_panels, &panel->node); dll_insert(&layout->open_panels, &min_panel->node); dll_insert(&layout->open_panels, &max_panel->node); // init min_panel panel->view->panel = min_panel; min_panel->parent = panel; min_panel->kind = PanelKind_Final; min_panel->view = panel->view; // init max_panel *new_panel_out = max_panel; max_panel->parent = panel; max_panel->kind = PanelKind_Final; max_panel->view = 0; // modify panel panel->kind = PanelKind_Intermediate; panel->tl_panel = min_panel; panel->br_panel = max_panel; panel->vertical_split = vertical_split; panel->split = make_panel_split_50_50(); // propogate rectangle sizes down from the new intermediate to // resize the panel and the new panel. layout_propogate_sizes_down_from_node(layout, panel); // update layout state layout->open_panel_count += 1; layout->active_panel = max_panel; layout->panel_state_dirty = true; result = true; } return(result); } internal b32 layout_close_panel(Layout *layout, Panel *panel){ b32 result = false; if (layout->open_panel_count > 1){ Panel *parent = panel->parent; Assert(parent != 0); // find sibling Panel *sibling = 0; if (parent->tl_panel == panel){ sibling = parent->br_panel; } else{ Assert(parent->br_panel == panel); sibling = parent->tl_panel; } // update layout state if (layout->active_panel == panel){ Panel *new_active = sibling; for (;new_active->kind == PanelKind_Intermediate;){ new_active = new_active->br_panel; } layout->active_panel = new_active; } layout->panel_state_dirty = true; layout->open_panel_count -= 1; // link grand parent and sibling Panel *g_parent = parent->parent; sibling->parent = g_parent; if (g_parent != 0){ if (g_parent->tl_panel == parent){ g_parent->tl_panel = sibling; } else{ Assert(g_parent->br_panel == parent); g_parent->br_panel = sibling; } } else{ Assert(parent == layout->root); layout->root = sibling; } // set sibling's size sibling->screen_region = parent->screen_region; // set the sizes down stream of sibling layout_propogate_sizes_down_from_node(layout, sibling); // free panel and parent layout__free_panel(layout, panel); layout__free_panel(layout, parent); result = true; } return(result); } internal Panel* layout_initialize(Partition *part, Layout *layout){ i32 panel_alloc_count = MAX_VIEWS*2 - 1; Panel *panels = push_array(part, Panel, panel_alloc_count); block_zero(panels, sizeof(*panels)*panel_alloc_count); layout->panel_first = panels; layout->panel_one_past_last = panels + panel_alloc_count; layout->margin = 3; layout->open_panel_count = 0; layout->open_panel_max_count = MAX_VIEWS; dll_init_sentinel(&layout->open_panels); dll_init_sentinel(&layout->intermediate_panels); Panel *panel = panels; layout->free_panels.next = &panel->node; panel->node.prev = &layout->free_panels; for (i32 i = 1; i < MAX_VIEWS; i += 1, panel += 1){ panel[1].node.prev = &panel[0].node; panel[0].node.next = &panel[1].node; } panel->node.next = &layout->free_panels; layout->free_panels.prev = &panel->node; panel = layout__alloc_panel(layout); panel->parent = 0; panel->kind = PanelKind_Final; panel->view = 0; block_zero_struct(&panel->screen_region); dll_insert(&layout->open_panels, &panel->node); layout->open_panel_count += 1; layout->root = panel; layout->active_panel = panel; layout->panel_state_dirty = true; return(panel); } internal void layout_set_margin(Layout *layout, i32 margin){ if (layout->margin != margin){ layout->margin = margin; layout__set_panel_rectangle(layout, layout->root, i32R(0, 0, layout->full_dim.x, layout->full_dim.y)); layout_propogate_sizes_down_from_node(layout, layout->root); layout->panel_state_dirty = true; } } internal void layout_set_root_size(Layout *layout, Vec2_i32 dim){ if (layout->full_dim != dim){ layout->full_dim = dim; layout__set_panel_rectangle(layout, layout->root, i32R(0, 0, dim.x, dim.y)); layout_propogate_sizes_down_from_node(layout, layout->root); layout->panel_state_dirty = true; } } internal Vec2_i32 layout_get_root_size(Layout *layout){ return(layout->full_dim); } internal i32 layout_get_absolute_position_of_split(Panel *panel){ i32 pos = 0; if (panel->vertical_split){ pos = layout__evaluate_split(panel->split, panel->rect_full.x0, panel->rect_full.x1); } else{ pos = layout__evaluate_split(panel->split, panel->rect_full.y0, panel->rect_full.y1); } return(pos); } internal Range layout__get_limiting_range_on_split_children(i32 mid, b32 vertical_split, Panel *panel, Range range){ if (panel->kind == PanelKind_Intermediate){ if (vertical_split == panel->vertical_split){ i32 pos = layout_get_absolute_position_of_split(panel); if (mid < pos && pos < range.max){ range.max = pos; } else if (range.min < pos && pos < mid){ range.min = pos; } } range = layout__get_limiting_range_on_split_children(mid, vertical_split, panel->tl_panel, range); range = layout__get_limiting_range_on_split_children(mid, vertical_split, panel->br_panel, range); } return(range); } internal Range layout_get_limiting_range_on_split(Layout *layout, Panel *panel){ // root level min max Range range = {}; if (panel->vertical_split){ range.max = layout->full_dim.x; } else{ range.max = layout->full_dim.y; } // get mid i32 mid = layout_get_absolute_position_of_split(panel); // parents min max for (Panel *panel_it = panel; panel_it != 0; panel_it = panel_it->parent){ if (panel->vertical_split == panel_it->vertical_split){ i32 pos = layout_get_absolute_position_of_split(panel_it); if (mid < pos && pos < range.max){ range.max = pos; } else if (range.min < pos && pos < mid){ range.min = pos; } } } // children min max if (panel->kind == PanelKind_Intermediate){ range = layout__get_limiting_range_on_split_children(mid, panel->vertical_split, panel->tl_panel, range); range = layout__get_limiting_range_on_split_children(mid, panel->vertical_split, panel->br_panel, range); } return(range); } internal void layout__reverse_evaluate_panel_split(Panel *panel, i32 position){ i32 v0 = 0; i32 v1 = 0; if (panel->vertical_split){ v0 = panel->rect_full.x0; v1 = panel->rect_full.x1; } else{ v0 = panel->rect_full.y0; v1 = panel->rect_full.y1; } switch (panel->split.kind){ case PanelSplitKind_Ratio_Min: { panel->split.v_f32 = unlerp((f32)v0, (f32)position, (f32)v1); }break; case PanelSplitKind_Ratio_Max: { panel->split.v_f32 = unlerp((f32)v1, (f32)position, (f32)v0); }break; case PanelSplitKind_FixedPixels_Min: { panel->split.v_i32 = clamp(v0, position, v1) - v0; }break; case PanelSplitKind_FixedPixels_Max: { panel->split.v_i32 = v1 - clamp(v0, position, v1); }break; } } internal void layout__set_split_absolute_position_inner(Panel *panel){ if (panel->kind == PanelKind_Intermediate){ i32_Rect r = panel->rect_full; i32 position = 0; if (panel->vertical_split){ position = layout__evaluate_split(panel->split, r.x0, r.x1); } else{ position = layout__evaluate_split(panel->split, r.y0, r.y1); } layout__reverse_evaluate_panel_split(panel, position); } } internal void layout_set_split_absolute_position(Layout *layout, Panel *panel, i32 absolute_position){ if (panel->kind == PanelKind_Intermediate){ layout__reverse_evaluate_panel_split(panel, absolute_position); layout__set_split_absolute_position_inner(panel->tl_panel); layout__set_split_absolute_position_inner(panel->br_panel); layout_propogate_sizes_down_from_node(layout, panel); layout->panel_state_dirty = true; } } internal Panel_ID panel_get_id(Layout *layout, Panel *panel){ Panel_ID id = 0; if (layout->panel_first <= panel && panel < layout->panel_one_past_last){ id = (Panel_ID)(panel - layout->panel_first) + 1; } return(id); } // BOTTOM