4coder/4ed_layout.cpp

527 lines
15 KiB
C++

/*
* Mr. 4th Dimention - Allen Webster
*
* 19.08.2015
*
* Panel layout and general view functions for 4coder
*
*/
// TOP
// TODO(allen):
//
// BUGS
//
struct Interactive_Style{
u32 bar_color;
u32 bar_active_color;
u32 base_color;
u32 pop1_color;
u32 pop2_color;
};
struct Interactive_Bar{
Interactive_Style style;
real32 pos_x, pos_y;
real32 text_shift_x, text_shift_y;
i32_Rect rect;
i16 font_id;
};
enum View_Message{
VMSG_STEP,
VMSG_DRAW,
VMSG_RESIZE,
VMSG_STYLE_CHANGE,
VMSG_FREE
};
struct View;
#define Do_View_Sig(name) \
i32 (name)(System_Functions *system, Exchange *exchange, \
View *view, i32_Rect rect, View *active, \
View_Message message, Render_Target *target, \
Input_Summary *user_input, Input_Summary *active_input)
typedef Do_View_Sig(Do_View_Function);
#define HANDLE_COMMAND_SIG(name) \
void (name)(System_Functions *system, View *view, \
Command_Data *command, Command_Binding binding, \
Key_Event_Data key, Key_Codes *codes)
typedef HANDLE_COMMAND_SIG(Handle_Command_Function);
// TODO(allen): this shouldn't exist
enum View_Type{
VIEW_TYPE_NONE,
VIEW_TYPE_FILE,
VIEW_TYPE_COLOR,
VIEW_TYPE_DEBUG,
VIEW_TYPE_INTERACTIVE,
VIEW_TYPE_MENU
};
struct Panel;
struct View{
union{
View *next_free;
View *major;
View *minor;
};
Panel *panel;
Command_Map *map;
Do_View_Function *do_view;
Handle_Command_Function *handle_command;
Mem_Options *mem;
i32 type;
i32 block_size;
Application_Mouse_Cursor mouse_cursor_type;
b32 is_active;
b32 is_minor;
};
struct Live_Views{
void *views;
View *free_view;
i32 count, max;
i32 stride;
};
struct Panel_Divider{
Panel_Divider *next_free;
i32 parent;
i32 which_child;
i32 child1, child2;
bool32 v_divider;
i32 pos;
};
struct Screen_Region{
i32_Rect full;
i32_Rect inner;
i32_Rect prev_inner;
i32 l_margin, r_margin;
i32 t_margin, b_margin;
};
struct Panel{
View *view;
i32 parent;
i32 which_child;
union{
struct{
i32_Rect full;
i32_Rect inner;
i32_Rect prev_inner;
i32 l_margin, r_margin;
i32 t_margin, b_margin;
};
Screen_Region screen_region;
};
};
struct Editing_Layout{
Panel *panels;
Panel_Divider *dividers;
Panel_Divider *free_divider;
i32 panel_count, panel_max_count;
i32 root;
i32 active_panel;
i32 full_width, full_height;
};
internal void
intbar_draw_string(Render_Target *target,
Interactive_Bar *bar, u8 *str, u32 char_color){
i16 font_id = bar->font_id;
for (i32 i = 0; str[i]; ++i){
char c = str[i];
font_draw_glyph(target, font_id, c,
bar->pos_x + bar->text_shift_x,
bar->pos_y + bar->text_shift_y,
char_color);
bar->pos_x += font_get_glyph_width(target, font_id, c);
}
}
internal void
intbar_draw_string(Render_Target *target, Interactive_Bar *bar,
String str, u32 char_color){
i16 font_id = bar->font_id;
for (i32 i = 0; i < str.size; ++i){
char c = str.str[i];
font_draw_glyph(target, font_id, c,
bar->pos_x + bar->text_shift_x,
bar->pos_y + bar->text_shift_y,
char_color);
bar->pos_x += font_get_glyph_width(target, font_id, c);
}
}
internal void
panel_init(Panel *panel){
*panel = {};
panel->parent = -1;
panel->l_margin = 3;
panel->r_margin = 3;
panel->t_margin = 3;
panel->b_margin = 3;
}
internal View*
live_set_get_view(Live_Views *live_set, i32 i){
void *result = ((char*)live_set->views + i*live_set->stride);
return (View*)result;
}
internal View*
live_set_alloc_view(Live_Views *live_set, Mem_Options *mem){
Assert(live_set->count < live_set->max);
View *result = 0;
result = live_set->free_view;
live_set->free_view = result->next_free;
memset(result, 0, live_set->stride);
++live_set->count;
result->is_active = 1;
result->mem = mem;
return result;
}
inline void
live_set_free_view(System_Functions *system, Exchange *exchange, Live_Views *live_set, View *view){
Assert(live_set->count > 0);
view->do_view(system, exchange, view, {}, 0, VMSG_FREE, 0, {}, 0);
view->next_free = live_set->free_view;
live_set->free_view = view;
--live_set->count;
view->is_active = 0;
}
inline void
view_replace_major(System_Functions *system, Exchange *exchange,
View *new_view, Panel *panel, Live_Views *live_set){
View *view = panel->view;
if (view){
if (view->is_minor && view->major){
live_set_free_view(system, exchange, live_set, view->major);
}
live_set_free_view(system, exchange, live_set, view);
}
new_view->panel = panel;
new_view->minor = 0;
panel->view = new_view;
}
inline void
view_replace_minor(System_Functions *system, Exchange *exchange,
View *new_view, Panel *panel, Live_Views *live_set){
View *view = panel->view;
new_view->is_minor = 1;
if (view){
if (view->is_minor){
new_view->major = view->major;
live_set_free_view(system, exchange, live_set, view);
}
else{
new_view->major = view;
view->is_active = 0;
}
}
else{
new_view->major = 0;
}
new_view->panel = panel;
panel->view = new_view;
}
inline void
view_remove_major(System_Functions *system, Exchange *exchange,
Panel *panel, Live_Views *live_set){
View *view = panel->view;
if (view){
if (view->is_minor && view->major){
live_set_free_view(system, exchange, live_set, view->major);
}
live_set_free_view(system, exchange, live_set, view);
}
panel->view = 0;
}
inline void
view_remove_major_leave_minor(System_Functions *system, Exchange *exchange,
Panel *panel, Live_Views *live_set){
View *view = panel->view;
if (view){
if (view->is_minor && view->major){
live_set_free_view(system, exchange, live_set, view->major);
view->major = 0;
}
else{
live_set_free_view(system, exchange, live_set, view);
panel->view = 0;
}
}
}
inline void
view_remove_minor(System_Functions *system, Exchange *exchange,
Panel *panel, Live_Views *live_set){
View *view = panel->view;
View *major = 0;
if (view){
if (view->is_minor){
major = view->major;
live_set_free_view(system, exchange, live_set, view);
}
}
panel->view = major;
if (major) major->is_active = 1;
}
inline void
view_remove(System_Functions *system, Exchange *exchange,
Panel *panel, Live_Views *live_set){
View *view = panel->view;
if (view->is_minor) view_remove_minor(system, exchange, panel, live_set);
else view_remove_major(system, exchange, panel, live_set);
}
struct Divider_And_ID{
Panel_Divider* divider;
i32 id;
};
internal Divider_And_ID
layout_alloc_divider(Editing_Layout *layout){
Assert(layout->free_divider);
Divider_And_ID result;
result.divider = layout->free_divider;
layout->free_divider = result.divider->next_free;
*result.divider = {};
result.divider->parent = -1;
result.divider->child1 = -1;
result.divider->child2 = -1;
result.id = (i32)(result.divider - layout->dividers);
if (layout->panel_count == 1){
layout->root = result.id;
}
return result;
}
internal Divider_And_ID
layout_get_divider(Editing_Layout *layout, i32 id){
Assert(id >= 0 && id < layout->panel_max_count-1);
Divider_And_ID result;
result.id = id;
result.divider = layout->dividers + id;
return result;
}
internal Panel*
layout_alloc_panel(Editing_Layout *layout){
Assert(layout->panel_count < layout->panel_max_count);
Panel *result = layout->panels + layout->panel_count;
*result = {};
++layout->panel_count;
return result;
}
internal void
layout_free_divider(Editing_Layout *layout, Panel_Divider *divider){
divider->next_free = layout->free_divider;
layout->free_divider = divider;
}
internal void
layout_free_panel(Editing_Layout *layout, Panel *panel){
Panel *panels = layout->panels;
i32 panel_count = --layout->panel_count;
i32 panel_i = (i32)(panel - layout->panels);
for (i32 i = panel_i; i < panel_count; ++i){
Panel *p = panels + i;
*p = panels[i+1];
if (p->view){
p->view->panel = p;
}
}
}
internal Divider_And_ID
layout_calc_divider_id(Editing_Layout *layout, Panel_Divider *divider){
Divider_And_ID result;
result.divider = divider;
result.id = (i32)(divider - layout->dividers);
return result;
}
struct Split_Result{
Panel_Divider *divider;
Panel *panel;
};
internal Split_Result
layout_split_panel(Editing_Layout *layout, Panel *panel, bool32 vertical){
Divider_And_ID div = layout_alloc_divider(layout);
if (panel->parent != -1){
Divider_And_ID pdiv = layout_get_divider(layout, panel->parent);
if (panel->which_child == -1){
pdiv.divider->child1 = div.id;
}
else{
pdiv.divider->child2 = div.id;
}
}
div.divider->parent = panel->parent;
div.divider->which_child = panel->which_child;
if (vertical){
div.divider->v_divider = 1;
div.divider->pos = (panel->full.x0 + panel->full.x1) / 2;
}
else{
div.divider->v_divider = 0;
div.divider->pos = (panel->full.y0 + panel->full.y1) / 2;
}
Panel *new_panel = layout_alloc_panel(layout);
panel->parent = div.id;
panel->which_child = -1;
new_panel->parent = div.id;
new_panel->which_child = 1;
Split_Result result;
result.divider = div.divider;
result.panel = new_panel;
return result;
}
internal void
panel_fix_internal_area(Panel *panel){
i32 left, right, top, bottom;
left = panel->l_margin;
right = panel->r_margin;
top = panel->t_margin;
bottom = panel->b_margin;
panel->inner.x0 = panel->full.x0 + left;
panel->inner.x1 = panel->full.x1 - right;
panel->inner.y0 = panel->full.y0 + top;
panel->inner.y1 = panel->full.y1 - bottom;
}
internal void
layout_fix_all_panels(Editing_Layout *layout){
Panel *panels = layout->panels;
if (layout->panel_count > 1){
Panel_Divider *dividers = layout->dividers;
int panel_count = layout->panel_count;
Panel *panel = panels;
for (i32 i = 0; i < panel_count; ++i){
i32 x0, x1, y0, y1;
x0 = 0;
x1 = x0 + layout->full_width;
y0 = 0;
y1 = y0 + layout->full_height;
i32 pos;
i32 which_child = panel->which_child;
Divider_And_ID div;
div.id = panel->parent;
for (;;){
div.divider = dividers + div.id;
pos = div.divider->pos;
div.divider = dividers + div.id;
// NOTE(allen): sorry if this is hard to read through, there are
// two binary conditionals that combine into four possible cases.
// Why am I appologizing to you? IF YOU CANT HANDLE MY CODE GET OUT!
i32 action = (div.divider->v_divider << 1) | (which_child > 0);
switch (action){
case 0: // v_divider : 0, which_child : -1
if (pos < y1) y1 = pos;
break;
case 1: // v_divider : 0, which_child : 1
if (pos > y0) y0 = pos;
break;
case 2: // v_divider : 1, which_child : -1
if (pos < x1) x1 = pos;
break;
case 3: // v_divider : 1, which_child : 1
if (pos > x0) x0 = pos;
break;
}
if (div.id != layout->root){
div.id = div.divider->parent;
which_child = div.divider->which_child;
}
else{
break;
}
}
panel->full.x0 = x0;
panel->full.y0 = y0;
panel->full.x1 = x1;
panel->full.y1 = y1;
panel_fix_internal_area(panel);
++panel;
}
}
else{
panels[0].full.x0 = 0;
panels[0].full.y0 = 0;
panels[0].full.x1 = layout->full_width;
panels[0].full.y1 = layout->full_height;
panel_fix_internal_area(panels);
}
}
internal void
layout_refit(Editing_Layout *layout,
i32 prev_x_off, i32 prev_y_off,
i32 prev_width, i32 prev_height){
Panel_Divider *dividers = layout->dividers;
i32 divider_max_count = layout->panel_max_count - 1;
i32 x_off = 0;
i32 y_off = 0;
real32 h_ratio = ((real32)layout->full_width) / prev_width;
real32 v_ratio = ((real32)layout->full_height) / prev_height;
for (i32 divider_id = 0; divider_id < divider_max_count; ++divider_id){
Panel_Divider *divider = dividers + divider_id;
if (divider->v_divider){
divider->pos = x_off + ROUND32((divider->pos - prev_x_off) * h_ratio);
}
else{
divider->pos = y_off + ROUND32((divider->pos - prev_y_off) * v_ratio);
}
}
layout_fix_all_panels(layout);
}
inline real32
view_base_compute_width(View *view){
Panel *panel = view->panel;
return (real32)(panel->inner.x1 - panel->inner.x0);
}
inline real32
view_base_compute_height(View *view){
Panel *panel = view->panel;
return (real32)(panel->inner.y1 - panel->inner.y0);
}
#define view_compute_width(view) (view_base_compute_width(&(view)->view_base))
#define view_compute_height(view) (view_base_compute_height(&(view)->view_base))
// BOTTOM